#include "SM_CommonFXPCH.h"
#include "SM_Engine3DPCH.h"
#include "MgcNaturalSpline3.h"
#include "SM_Spline.h"
#include "MBinArray.h"


/*
struct GRIDCELL
{
   float*    f[8];
};



#define     MAX_BATCH 1000




unsigned        m_uStored;
MeshElement     m_MeshElement;
int             m_iShader;
static Vector3D v3dOffsets[8];  
static Vector3D v3dBase;


#define GRIDSIZE  128
#define GRIDSHIFT 7

// Potencial evaluation
#define META_C      1.0f
#define POTENTIAL   0.1f


//
// Voxel mask functionality. Sets bits to mark voxels as already 
// visited
//
#define MASKSIZE (GRIDSIZE*GRIDSIZE*GRIDSIZE)/(sizeof(unsigned)<<3)
unsigned g_uVoxelMask[MASKSIZE];

void     ClearVoxelMask()
{
  memset(g_uVoxelMask, 0 , MASKSIZE*sizeof(unsigned));
}

__forceinline int VoxelMaskIsSet(int x, int y, int z)
{
  assert(x >= 0 && x < GRIDSIZE &&
         y >= 0 && y < GRIDSIZE &&
         z >= 0 && z < GRIDSIZE);

  int iVoxel = x + (y<<GRIDSHIFT) + (z<<(GRIDSHIFT*2));
  int iIndex = iVoxel >> 5;
  int iBit   = iVoxel & 31;

  return g_uVoxelMask[iIndex] & (1<<iBit);
}      

__forceinline int VoxelMaskIsSetThenSet(int x, int y, int z)
{
  assert(x >= 0 && x < GRIDSIZE &&
         y >= 0 && y < GRIDSIZE &&
         z >= 0 && z < GRIDSIZE);

  int iVoxel = x + (y<<GRIDSHIFT) + (z<<(GRIDSHIFT*2));
  int iIndex = iVoxel >> 5;
  int iBit   = iVoxel & 31;


  int iReturn = g_uVoxelMask[iIndex] & (1<<iBit);
  g_uVoxelMask[iIndex] |= (1<<iBit);

  return iReturn;
}        

__forceinline void VoxelMaskSet(int x, int y, int z)
{
  assert(x >= 0 && x < GRIDSIZE &&
         y >= 0 && y < GRIDSIZE &&
         z >= 0 && z < GRIDSIZE);

  int iVoxel = x + (y<<GRIDSHIFT) + (z<<(GRIDSHIFT*2));
  int iIndex = iVoxel >> 5;
  int iBit   = iVoxel & 31;

  g_uVoxelMask[iIndex] |= (1<<iBit);
}

// Metaball positions
#define MAX_METABALLS 64

struct Metaball
{
  Vector3D m_v3dPosition;
};

int      g_uMetaballs = 0;
Metaball g_Metaballs[MAX_METABALLS];

void AddMetaball(Metaball* pMetaball)
{
  if (g_uMetaballs == MAX_METABALLS)
  {
    return;
  }

  g_Metaballs[g_uMetaballs++] = *pMetaball;
}



float Potential(Vector3D& v3dPos)
{
  int i;

  float fPotential = 0.0f;
  
  for (i = 0 ; i < g_uMetaballs ; i++)
  {
    fPotential += META_C / (1.0f+(v3dPos - g_Metaballs[i].m_v3dPosition).SquaredLength());        
  }

  return fPotential;
}

float PotentialNormal(Vector3D& v3dPos, Vector3D& outNormal)
{
  int i;

  float fPotential = 0.0f;

  outNormal = Vector3D(0.0f, 0.0f, 0.0f);
  
  for (i = 0 ; i < g_uMetaballs ; i++)
  {
    Vector3D v3dDiff = v3dPos - g_Metaballs[i].m_v3dPosition;
    float fCurrent = META_C / (1.0f+v3dDiff.SquaredLength());
    fPotential += fCurrent;    
    outNormal += v3dDiff.Normalize()*fCurrent;    
  }

  return fPotential;
}



float g_fVoxelPotentials[8];
Vector3D g_v3dNormals[8];

unsigned CalculateVoxelMask()
{
  unsigned u = 0;

  u |= (g_fVoxelPotentials[0] < POTENTIAL)?(1<<0):0;
  u |= (g_fVoxelPotentials[1] < POTENTIAL)?(1<<1):0;
  u |= (g_fVoxelPotentials[2] < POTENTIAL)?(1<<2):0;
  u |= (g_fVoxelPotentials[3] < POTENTIAL)?(1<<3):0;
  u |= (g_fVoxelPotentials[4] < POTENTIAL)?(1<<4):0;
  u |= (g_fVoxelPotentials[5] < POTENTIAL)?(1<<5):0;
  u |= (g_fVoxelPotentials[6] < POTENTIAL)?(1<<6):0;
  u |= (g_fVoxelPotentials[7] < POTENTIAL)?(1<<7):0;

  return u;
}
   
unsigned CalculateVoxelPotentials(int x, int y, int z)
{  
  g_fVoxelPotentials[0] = PotentialNormal(Vector3D(x  , y  , z  ), g_v3dNormals[0]);
  g_fVoxelPotentials[1] = PotentialNormal(Vector3D(x  , y+1, z  ), g_v3dNormals[1]);
  g_fVoxelPotentials[2] = PotentialNormal(Vector3D(x  , y  , z+1), g_v3dNormals[2]);
  g_fVoxelPotentials[3] = PotentialNormal(Vector3D(x  , y+1, z+1), g_v3dNormals[3]);
  g_fVoxelPotentials[4] = PotentialNormal(Vector3D(x+1, y  , z  ), g_v3dNormals[4]);
  g_fVoxelPotentials[5] = PotentialNormal(Vector3D(x+1, y+1, z  ), g_v3dNormals[5]);
  g_fVoxelPotentials[6] = PotentialNormal(Vector3D(x+1, y  , z+1), g_v3dNormals[6]);
  g_fVoxelPotentials[7] = PotentialNormal(Vector3D(x+1, y+1, z+1), g_v3dNormals[7]);  
                      

  return CalculateVoxelMask();
}



#define MAXVOXELS 8192

unsigned g_uVoxelList[MAXVOXELS*3];
unsigned g_uVoxels = 0;

bool AddVoxel(int x, int y, int z)
{
  assert(g_uVoxels < MAXVOXELS);

  if (x < 0 || y < 0 || z < 0 ||
      x > GRIDSIZE-1 || y > GRIDSIZE-1 || z > GRIDSIZE-1 ||
      VoxelMaskIsSetThenSet(x, y, z))
  {
    return false;
  }
    
  g_uVoxelList[g_uVoxels*3+0] = x;
  g_uVoxelList[g_uVoxels*3+1] = y;
  g_uVoxelList[g_uVoxels*3+2] = z;    

  VoxelMaskSet(x, y, z);

  g_uVoxels++;
  return true;
}

void GetVoxel(int& x, int &y, int &z)
{
  assert(g_uVoxels != 0);
  
  g_uVoxels--;

  x = g_uVoxelList[g_uVoxels*3+0];
  y = g_uVoxelList[g_uVoxels*3+1];
  z = g_uVoxelList[g_uVoxels*3+2];  
}

void  AddNeighbours(int x, int  y, int z)
{
  AddVoxel(x-1, y-1, z-1);
  AddVoxel(x  , y-1, z-1);
  AddVoxel(x+1, y-1, z-1);
  AddVoxel(x-1, y  , z-1);
  AddVoxel(x  , y  , z-1);
  AddVoxel(x+1, y  , z-1);
  AddVoxel(x-1, y+1, z-1);
  AddVoxel(x  , y+1, z-1);
  AddVoxel(x+1, y+1, z-1);

  AddVoxel(x-1, y-1, z);
  AddVoxel(x  , y-1, z);
  AddVoxel(x+1, y-1, z);
  AddVoxel(x-1, y  , z);
  AddVoxel(x+1, y  , z);
  AddVoxel(x-1, y+1, z);
  AddVoxel(x  , y+1, z);
  AddVoxel(x+1, y+1, z);

  AddVoxel(x-1, y-1, z+1);
  AddVoxel(x  , y-1, z+1);
  AddVoxel(x+1, y-1, z+1);
  AddVoxel(x-1, y  , z+1);
  AddVoxel(x  , y  , z+1);
  AddVoxel(x+1, y  , z+1);
  AddVoxel(x-1, y+1, z+1);
  AddVoxel(x  , y+1, z+1);
  AddVoxel(x+1, y+1, z+1);
}

//FindIntersection
bool FindIntersection(int iMetaball)
{

  int x, y, z;

  x = int(g_Metaballs[iMetaball].m_v3dPosition.x);
  y = int(g_Metaballs[iMetaball].m_v3dPosition.y);
  z = int(g_Metaballs[iMetaball].m_v3dPosition.z);

  if (x < 0 || y < 0 || z < 0 ||
      x > GRIDSIZE-1 || y > GRIDSIZE-1 || z > GRIDSIZE-1)
  {
    return false;
  }

  while (x < GRIDSIZE)
  {
    unsigned uMask = CalculateVoxelPotentials(x, y, z);

    if (uMask && (uMask != 0xFF))
    {
      return AddVoxel(x, y, z);
    }

    x++;
  }

  return false;
}

class IsoSurfaceFX : public SM_DemoEffect
{
public:              
  IsoSurfaceFX(char const* pcName) : SM_DemoEffect(pcName)
  {
  }

  virtual          ~IsoSurfaceFX()
  {
  }

    
  
  int      Init(const char* pcCommand)
  {
    float fTime=0.0f;
        
    
    m_iShader=ShaderManager::LoadShader("enviroment");
    SM_D3d::SetRenderState(D3DRS_LIGHTING, FALSE);


    m_MeshElement.m_iShader           =m_iShader;        
    m_MeshElement.m_iVB               =-1;
    m_MeshElement.m_iIB               =-1;
    m_MeshElement.m_pVertices         =new FVF_PosNormalDiffuseTex1[MAX_BATCH]; 
    if (!m_MeshElement.m_pVertices) return -1;

    m_MeshElement.m_pIndices          =new unsigned short[MAX_BATCH];
    if (!m_MeshElement.m_pIndices) return -1;

    int i;
    for (i=0 ; i<MAX_BATCH ; i++)
    {
      m_MeshElement.m_pIndices[i]=i;
    }

    m_MeshElement.m_uStartVertex      =0;
    m_MeshElement.m_uVertices         =0;
    m_MeshElement.m_uStartIndex       =0;
    m_MeshElement.m_uPrimitives       =0;
    m_MeshElement.m_WorldTransform    =Matrix4X4::Identity;
    m_MeshElement.m_uActiveLightMask  =0;  
    m_MeshElement.m_fDepth            =0.0f;

    v3dOffsets[0]=Vector3D(0.0f, 0.0f, 0.0f );
    v3dOffsets[1]=Vector3D(0.0f, 1.0f, 0.0f );
    v3dOffsets[2]=Vector3D(0.0f, 0.0f, 1.0f);
    v3dOffsets[3]=Vector3D(0.0f, 1.0f, 1.0f);
    v3dOffsets[4]=Vector3D(1.0f, 0.0f, 0.0f );
    v3dOffsets[5]=Vector3D(1.0f, 1.0f, 0.0f );
    v3dOffsets[6]=Vector3D(1.0f, 0.0f, 1.0f);
    v3dOffsets[7]=Vector3D(1.0f, 1.0f, 1.0f);

    
    
    
    return (0);
  }

  int      Shutdown()
  {
    if (m_MeshElement.m_pVertices)
    {
        delete[] m_MeshElement.m_pVertices;
        m_MeshElement.m_pVertices=0; 
    }

    if (m_MeshElement.m_pIndices)
    {
      delete[] m_MeshElement.m_pIndices;
      m_MeshElement.m_pIndices=0;

    }
    return (0);
  }

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

  int      Stop()
  {
    return (0);
  }

  int      Reset()
  {
    return (0);
  }

  void Flush()
  {

    //m_uStored=0;
    //return;

    m_MeshElement.m_uVertices  =m_uStored;
    m_MeshElement.m_uPrimitives=m_uStored/3;

    RenderPipeline::Render(&m_MeshElement);
    RenderPipeline::Flush();    

    m_uStored=0;
  }


  
  
  static void VertexInterp(FVF_PosNormalDiffuseTex1* pVertex, int i1, int i2)
  {
    float fLerp=(POTENTIAL-g_fVoxelPotentials[i1])/(g_fVoxelPotentials[i2]-g_fVoxelPotentials[i1]);

    assert(fLerp>=0.0f && fLerp<=1.0f);

    Vector3D vPos = v3dBase + v3dOffsets[i1] + (v3dOffsets[i2] - v3dOffsets[i1])*fLerp;
    pVertex->x = vPos.x;
    pVertex->y = vPos.y;
    pVertex->z = vPos.z;

    
    Vector3D Normal = g_v3dNormals[i1]+(g_v3dNormals[i2]-g_v3dNormals[i1])*fLerp;                     
    Normal.Normalize();

    pVertex->diffuse = 0xFFFFFFFF;
    
    pVertex->nx=Normal.x;
    pVertex->ny=Normal.y;
    pVertex->nz=Normal.z;
  }
  
  
  static void SpitTri(int v0, int v1, int v2, int v3, unsigned uMask)
  {
    unsigned uSelector  = ((uMask&(1<<(v0)))>>(v0))<<0;
             uSelector |= ((uMask&(1<<(v1)))>>(v1))<<1;
             uSelector |= ((uMask&(1<<(v2)))>>(v2))<<2;
             uSelector |= ((uMask&(1<<(v3)))>>(v3))<<3;

    FVF_PosNormalDiffuseTex1* pVertex=m_MeshElement.m_pVertices+m_uStored;
    switch (uSelector) 
    {
     case 0x0E:
     case 0x01:
        VertexInterp(pVertex, v0,v1); pVertex++;
        VertexInterp(pVertex, v0,v2); pVertex++;
        VertexInterp(pVertex, v0,v3); pVertex++;
        m_uStored+=3;
        break;
     case 0x0D:
     case 0x02:
        VertexInterp(pVertex, v1,v0); pVertex++;
        VertexInterp(pVertex, v1,v3); pVertex++;
        VertexInterp(pVertex, v1,v2); pVertex++;
        m_uStored+=3;
        break;
        
     case 0x0C:
     case 0x03:
        VertexInterp(pVertex, v0,v3); pVertex++;
        VertexInterp(pVertex, v0,v2); pVertex++;
        VertexInterp(pVertex, v1,v3); pVertex++;
        m_uStored+=3;
        VertexInterp(pVertex, v1,v3); pVertex++;
        VertexInterp(pVertex, v1,v2); pVertex++;
        VertexInterp(pVertex, v0,v2); pVertex++;
        m_uStored+=3;
        break;
       
     case 0x0B:
     case 0x04:
        VertexInterp(pVertex, v2,v0); pVertex++;
        VertexInterp(pVertex, v2,v1); pVertex++;
        VertexInterp(pVertex, v2,v3); pVertex++;        
        m_uStored+=3;
        break;        
     case 0x0A:
     case 0x05:
        VertexInterp(pVertex, v0,v1); pVertex++;
        VertexInterp(pVertex, v2,v3); pVertex++;
        VertexInterp(pVertex, v0,v3); pVertex++;
        
        m_uStored+=3;
        VertexInterp(pVertex, v0,v1); pVertex++;
        VertexInterp(pVertex, v1,v2); pVertex++;                
        VertexInterp(pVertex, v2,v3); pVertex++;                
        m_uStored+=3;
        break;
     case 0x09:
     case 0x06:
        VertexInterp(pVertex, v0,v1); pVertex++;
        VertexInterp(pVertex, v1,v3); pVertex++;
        VertexInterp(pVertex, v2,v3); pVertex++;                
        m_uStored+=3;

        VertexInterp(pVertex, v0,v1); pVertex++;
        VertexInterp(pVertex, v0,v2); pVertex++;
        VertexInterp(pVertex, v2,v3); pVertex++;                
        m_uStored+=3;
        break;
     case 0x07:
     case 0x08:
        VertexInterp(pVertex, v3, v0); pVertex++;
        VertexInterp(pVertex, v3, v2); pVertex++;
        VertexInterp(pVertex, v3, v1); pVertex++;        
        m_uStored+=3;
        
        break;       
     }
  }


  void Polygonize(int i, int j, int k, unsigned uMask)
  {      
    v3dBase=Vector3D(i, j, k);
          
    SpitTri(0,2,4,5, uMask); 
    SpitTri(2,4,5,6, uMask); 
    SpitTri(2,5,6,7, uMask); 
    SpitTri(2,3,5,7, uMask); 
    SpitTri(0,1,2,5, uMask); 
    SpitTri(1,2,3,5, uMask); 
    
  }

  
  void Clear()
  {   
  }  

  int      Run(float fTime)
  {
    g_uMetaballs = 0;

    Metaball m;
    m.m_v3dPosition = Vector3D(GRIDSIZE/2.0f+sinf(fTime)*(GRIDSIZE/32.0f), GRIDSIZE/2.0f, GRIDSIZE/2.0f);
    AddMetaball(&m);

    m.m_v3dPosition = Vector3D(5.0f+GRIDSIZE/2.0f, GRIDSIZE/5.0f, GRIDSIZE/2.0f);
    AddMetaball(&m);

    m.m_v3dPosition = Vector3D(GRIDSIZE/2.0f+sinf(fTime*1.2f)*(GRIDSIZE/14.0f), GRIDSIZE/2.0f+cosf(fTime*1.2f)*(GRIDSIZE/14.0f), GRIDSIZE/2.0f);
    AddMetaball(&m);

    m.m_v3dPosition = Vector3D(GRIDSIZE/2.0f+sinf(fTime)*(GRIDSIZE/16.0f), GRIDSIZE/2.0f+sinf(fTime)*(GRIDSIZE/16.0f), GRIDSIZE/2.0f);
    AddMetaball(&m);

    m.m_v3dPosition = Vector3D(GRIDSIZE/2.0f+cosf(fTime)*(GRIDSIZE/16.0f), GRIDSIZE/2.0f+cosf(fTime)*(GRIDSIZE/16.0f), GRIDSIZE/2.0f);
    AddMetaball(&m);

    m.m_v3dPosition = Vector3D(GRIDSIZE/2.0f+sinf(fTime*1.3f)*(GRIDSIZE/32.0f), GRIDSIZE/2.0f, GRIDSIZE/2.0f+cosf(fTime*1.3f)*(GRIDSIZE/20.0f));
    AddMetaball(&m);

    

    RenderContext RC;   

    //float fRot=0.0f;//
    float fRot=fTime*1.25f;
    Vector3D v3dVPN=Vector3D(sinf(fRot), 0.0f, cosf(fRot));
    v3dVPN.Normalize();
    Vector3D v3dVUP=Vector3D(0.0f, -1.0f, 0.0f);
    Vector3D v3dVRP=Vector3D::Cross(v3dVUP, v3dVPN);
    
    Quaternion q;
    q.FromFrame(v3dVPN, v3dVUP, v3dVRP);



    RC.Set(
    //Vector3D(0.0f, 0.0f, -1.8f+0.0f*sinf(fTime*0.05)),
    Vector3D(3.0f*sinf(fRot), 0.0f, 3.0f*cosf(fRot)),
    //Quaternion(1.0f, 0.0f, 0.0f, 0.0f),
    q*Quaternion(3.141592f, Vector3D(1.0f, 0.0f, 0.0f)),
    60,
    0.75f,
    0.01f,
    200.0f);

    RC.Set(
    Vector3D(GRIDSIZE/2, GRIDSIZE/2, GRIDSIZE/2.5f-12.0f),
    Quaternion(1.0f, 0.0f, 0.0f, 0.0f),
    90,
    0.75f,
    1.0f,
    200.0f);
  
    RC.SetViewport(0.0f, 0.0f, 640, 480);
    RC.SyncRasterizer();
    RC.UpdateFrustum();

    ShaderManager::SetRenderContext(&RC);
      
    int Add=0;
    
    
    ClearVoxelMask();

    int i;
    for (i = 0 ; i < g_uMetaballs ; i++)
    {
      if (FindIntersection(i))
      {
        while (g_uVoxels)
        {
          int x, y, z;

          GetVoxel(x, y, z);

          unsigned uMask = CalculateVoxelPotentials(x, y, z);

          if (uMask && uMask != 0xFF)
          {
            // Did we fill the batch?
            if (m_uStored>=MAX_BATCH-12*3)  // 12 is the max tris we can output for one cube
            {
              Flush();
            }

            Polygonize(x, y, z, uMask);
            
            AddNeighbours(x, y, z);
          }          
        }        
      }            
    }
      
    if (m_uStored)
    {
      Flush();
    }    
    
    return (1);
  }

  int      Command           (float fTime, const char* pcCommand)
  {
    return (0);
  }    
};

DEFINE_EFFECT(IsoSurfaceFX)
IsoSurfaceFX IsoSurface("ISOSURFACE");
*/

