#include "SM_Engine3DPCH.h"
#include "SM_Voxelizer.h"
#include "MBinArray.h"


class VoxelizerCache : public MeshElementCache
{
public:
  VoxelizerCache  ()
  {
    m_ppMeshElements = 0;
    m_uMeshElements = 0;
  }

  ~VoxelizerCache ()
  {
    Shutdown();
  }

  int  Init()
  {
    return 0;
  }

  int  Shutdown()
  {
    unsigned i;

    for (i = 0 ; i < m_uMeshElements ; i++)
    {
      //
      delete[] m_ppMeshElements[i]->m_pVertices;
      delete[] m_ppMeshElements[i]->m_pIndices;

      ResourceManager::ReleaseVertexBuffer(m_ppMeshElements[i]->m_iVB);
      ResourceManager::ReleaseIndexBuffer(m_ppMeshElements[i]->m_iIB);
       
      delete m_ppMeshElements[i];
    }

    delete[] m_ppMeshElements;

    return 0;
  }
  
  void Render(RenderContext* pRenderContext, int iOutcode)
  {
    if (!m_uMeshElements) return;

    ShaderManager::Shader* pShader = ShaderManager::GetShader(m_ppMeshElements[0]->m_iShader);
    
    pShader->m_Flags &= ~ShaderManager::SH_VOXELIZE;
    /*
    int i;
    for (i = 0 ; i < m_uMeshElements ; i++)
    {
      pShader->Render(&m_ppMeshElements[i], 1, true);
    }
    */
    pShader->Render(m_ppMeshElements, m_uMeshElements, true);
    
    pShader->m_Flags |= ShaderManager::SH_VOXELIZE;    
  }  

  void PreCache(MeshElement* pME)
  {
    SM_Voxelizer::CacheMeshElement(pME);
  }

  MeshElement** m_ppMeshElements;
  unsigned      m_uMeshElements;
};


namespace SM_Voxelizer
{
  #define MAX_VERTS 4000
  MeshElement*      g_pME;
  RenderContext*    g_pRC;

  // Planes that define triangle
  Vector3D                  Transformed[MAX_VERTS];   // Transformed vertices
  Vector3D                  *pTA, *pTB, *pTC;
  FVF_PosNormalDiffuseTex1  *pVA, *pVB, *pVC;
  float                     fGrain = 1.0f;
  

  #define MAX_CUBES 1500
  

  // We want to have as much stuff precomputed as we can. For the moment, 2 big types. Flat shaded cubes
  // and textured cubes

  // Flat cubes
  FVF_PosNormalDiffuseTex1 g_pVertices[MAX_CUBES*8];
  unsigned short           g_pusIndices[MAX_CUBES*36];  // 3 indices * 6 faces * 2 tris/face
  
  // Diffuse cubes
  FVF_PosNormalDiffuseTex1 g_pTexturedVertices[MAX_CUBES*12];
  unsigned short           g_pusTexturedIndices[MAX_CUBES*36];  // 3 indices * 6 faces * 2 tris/face

  // Environment cubes
  FVF_PosNormalDiffuseTex1 g_pEnvironmentVertices[MAX_CUBES*24];
  unsigned short           g_pusEnvironmentIndices[MAX_CUBES*36];  // 3 indices * 6 faces * 2 tris/face

  
  
  bool                        g_bCreatingCache;
  MBinArray<MeshElement*>     g_CacheArray;
  MeshElement                 g_me;
  unsigned                    g_uCurrent = 0;
  ShaderManager::Shader*      g_pShader;

  enum eRenderType
  {
    SOLIDFLAT,
    TEXTURE,
    ENVIRONMENT,
  };

  eRenderType              g_RenderType;
  
  void InitFlatCubeBuffers()
  {
    FVF_PosNormalDiffuseTex1* pVertices;
    unsigned short*           pusIndices;

    int i;

    for (i = 0, pVertices = g_pVertices ; i < MAX_CUBES ; i++, pVertices += 8)
    {
      // Fill out vertices
      pVertices[0].nx=0.0f        ; pVertices[0].ny=0.0f        ; pVertices[0].nz=-1.0f;
      pVertices[0].u =0.0f        ; pVertices[0].v =0.0f        ; pVertices[0].diffuse=0xFFFFFFFF;

      pVertices[1].nx=0.0f        ; pVertices[1].ny=0.0f        ; pVertices[1].nz=0.0f;
      pVertices[1].u =0.0f        ; pVertices[1].v =0.0f        ; pVertices[1].diffuse=0xFFFFFFFF;

      pVertices[2].nx=-1.0f       ; pVertices[2].ny=0.0f        ; pVertices[2].nz=0.0f;
      pVertices[2].u =0.0f        ; pVertices[2].v =0.0f        ; pVertices[2].diffuse=0xFFFFFFFF;

      pVertices[3].nx=0.0f        ; pVertices[3].ny=0.0f        ; pVertices[3].nz=0.0f;
      pVertices[3].u =0.0f        ; pVertices[3].v =0.0f        ; pVertices[3].diffuse=0xFFFFFFFF;

      pVertices[4].nx=0.0f        ; pVertices[4].ny=0.0f        ; pVertices[4].nz=1.0f;
      pVertices[4].u =0.0f        ; pVertices[4].v =0.0f        ; pVertices[4].diffuse=0xFFFFFFFF;

      pVertices[5].nx=0.0f        ; pVertices[5].ny=-1.0f        ; pVertices[5].nz=0.0f;
      pVertices[5].u =0.0f        ; pVertices[5].v =0.0f        ; pVertices[5].diffuse=0xFFFFFFFF;

      pVertices[6].nx=0.0f        ; pVertices[6].ny=1.0f       ; pVertices[6].nz=0.0f;
      pVertices[6].u =0.0f        ; pVertices[6].v =0.0f        ; pVertices[6].diffuse=0xFFFFFFFF;

      pVertices[7].nx=1.0f        ; pVertices[7].ny=0.0f        ; pVertices[7].nz=0.0f;
      pVertices[7].u =0.0f        ; pVertices[7].v =0.0f        ; pVertices[7].diffuse=0xFFFFFFFF;
    }

    unsigned short pusCanonicIndices[36]={  0, 3, 2,
					                                  0, 1, 3,
					                                  4, 7, 5,
					                                  4, 6, 7,
					                                  5, 1, 0,
					                                  5, 0, 4,
					                                  7, 3, 1,
					                                  7, 1, 5,
					                                  6, 2, 3,
					                                  6, 3, 7,
					                                  2, 4, 0,
                                            2, 6, 4};


    for (i = 0, pusIndices = g_pusIndices ; i < MAX_CUBES ; i++, pusIndices+=36)
    {
      int j;

      for (j = 0 ; j < 36 ; j++)
      {
        pusIndices[j] = pusCanonicIndices[j]+i*8;
      }
    }
  }

  void InitTexturedCubeBuffers()
  {
    FVF_PosNormalDiffuseTex1* pVertices;
    unsigned short*           pusIndices;

    int i;

    for (i = 0, pVertices = g_pTexturedVertices ; i < MAX_CUBES ; i++, pVertices += 12)
    {
      // Fill out vertices
      pVertices[0].nx=0.0f        ; pVertices[0].ny=0.0f        ; pVertices[0].nz=-1.0f;
      pVertices[0].u =0.0f        ; pVertices[0].v =0.0f        ; pVertices[0].diffuse=0xFFFFFFFF;

      pVertices[1].nx=0.0f        ; pVertices[1].ny=0.0f        ; pVertices[1].nz=0.0f;
      pVertices[1].u =1.0f        ; pVertices[1].v =0.0f        ; pVertices[1].diffuse=0xFFFFFFFF;

      pVertices[2].nx=-1.0f       ; pVertices[2].ny=0.0f        ; pVertices[2].nz=0.0f;
      pVertices[2].u =0.0f        ; pVertices[2].v =1.0f        ; pVertices[2].diffuse=0xFFFFFFFF;

      pVertices[3].nx=0.0f        ; pVertices[3].ny=0.0f        ; pVertices[3].nz=0.0f;
      pVertices[3].u =1.0f        ; pVertices[3].v =1.0f        ; pVertices[3].diffuse=0xFFFFFFFF;

      pVertices[4].nx=0.0f        ; pVertices[4].ny=0.0f        ; pVertices[4].nz=1.0f;
      pVertices[4].u =0.0f        ; pVertices[4].v =1.0f        ; pVertices[4].diffuse=0xFFFFFFFF;

      pVertices[5].nx=0.0f        ; pVertices[5].ny=-1.0f        ; pVertices[5].nz=0.0f;
      pVertices[5].u =1.0f        ; pVertices[5].v =1.0f        ; pVertices[5].diffuse=0xFFFFFFFF;

      pVertices[6].nx=0.0f        ; pVertices[6].ny=1.0f       ; pVertices[6].nz=0.0f;
      pVertices[6].u =0.0f        ; pVertices[6].v =0.0f        ; pVertices[6].diffuse=0xFFFFFFFF;

      pVertices[7].nx=1.0f        ; pVertices[7].ny=0.0f        ; pVertices[7].nz=0.0f;
      pVertices[7].u =1.0f        ; pVertices[7].v =0.0f        ; pVertices[7].diffuse=0xFFFFFFFF;      

      pVertices[8].nx=0.0f        ; pVertices[8].ny=0.0f        ; pVertices[8].nz=-1.0f;
      pVertices[8].u =1.0f        ; pVertices[8].v =1.0f        ; pVertices[8].diffuse=0xFFFFFFFF;

      pVertices[9].nx=0.0f        ; pVertices[9].ny=0.0f        ; pVertices[9].nz=0.0f;
      pVertices[9].u =0.0f        ; pVertices[9].v =1.0f        ; pVertices[9].diffuse=0xFFFFFFFF;

      pVertices[10].nx=0.0f       ; pVertices[10].ny=0.0f        ; pVertices[10].nz=1.0f;
      pVertices[10].u =1.0f        ; pVertices[10].v =0.0f        ; pVertices[10].diffuse=0xFFFFFFFF;

      pVertices[11].nx=0.0f        ; pVertices[11].ny=0.0f        ; pVertices[11].nz=0.0f;
      pVertices[11].u =0.0f        ; pVertices[11].v =0.0f        ; pVertices[11].diffuse=0xFFFFFFFF;

    }

    unsigned short pusCanonicIndices[36]={  0, 3, 2,
					                                  0, 1, 3,
					                                  4, 7, 5,
					                                  4, 6, 7,
					                                  5, 1, 0,
					                                  5, 0, 4,
					                                  6, 2, 3,
					                                  6, 3, 7,
			                                      7,11, 9,
					                                  7, 9, 5,		
					                                 10, 4, 8,
                                           10, 6, 4};


    for (i = 0, pusIndices = g_pusTexturedIndices ; i < MAX_CUBES ; i++, pusIndices+=36)
    {
      int j;

      for (j = 0 ; j < 36 ; j++)  
      {
        pusIndices[j] = pusCanonicIndices[j]+i*12;
      }
    }
  }

  void InitEnvironmentCubeBuffers()
  {
    FVF_PosNormalDiffuseTex1* pVertices;
    unsigned short*           pusIndices;

    int i;

    for (i = 0, pVertices = g_pEnvironmentVertices ; i < MAX_CUBES ; i++, pVertices += 24)
    {
      // Fill out vertices
      pVertices[0].nx=0.0f        ; pVertices[0].ny=0.0f        ; pVertices[0].nz=-1.0f;
      pVertices[0].u =0.0f        ; pVertices[0].v =0.0f        ; pVertices[0].diffuse=0xFFFFFFFF;

      pVertices[1].nx=0.0f        ; pVertices[1].ny=0.0f        ; pVertices[1].nz=-1.0f;
      pVertices[1].u =1.0f        ; pVertices[1].v =0.0f        ; pVertices[1].diffuse=0xFFFFFFFF;

      pVertices[2].nx=0.0f        ; pVertices[2].ny=0.0f        ; pVertices[2].nz=-1.0f;
      pVertices[2].u =0.0f        ; pVertices[2].v =1.0f        ; pVertices[2].diffuse=0xFFFFFFFF;

      pVertices[3].nx=0.0f        ; pVertices[3].ny=0.0f        ; pVertices[3].nz=-1.0f;
      pVertices[3].u =1.0f        ; pVertices[3].v =1.0f        ; pVertices[3].diffuse=0xFFFFFFFF;


      pVertices[4].nx=0.0f        ; pVertices[4].ny=0.0f        ; pVertices[4].nz=1.0f;
      pVertices[4].u =0.0f        ; pVertices[4].v =0.0f        ; pVertices[4].diffuse=0xFFFFFFFF;

      pVertices[5].nx=0.0f        ; pVertices[5].ny=0.0f       ; pVertices[5].nz=1.0f;
      pVertices[5].u =1.0f        ; pVertices[5].v =0.0f        ; pVertices[5].diffuse=0xFFFFFFFF;

      pVertices[6].nx=0.0f        ; pVertices[6].ny=0.0f        ; pVertices[6].nz=1.0f;
      pVertices[6].u =0.0f        ; pVertices[6].v =1.0f        ; pVertices[6].diffuse=0xFFFFFFFF;

      pVertices[7].nx=0.0f        ; pVertices[7].ny=0.0f        ; pVertices[7].nz=1.0f;
      pVertices[7].u =1.0f        ; pVertices[7].v =1.0f        ; pVertices[7].diffuse=0xFFFFFFFF;


      pVertices[8].nx=1.0f        ; pVertices[8].ny=0.0f        ; pVertices[8].nz=0.0f;
      pVertices[8].u =0.0f        ; pVertices[8].v =0.0f        ; pVertices[8].diffuse=0xFFFFFFFF;

      pVertices[9].nx=1.0f        ; pVertices[9].ny=0.0f        ; pVertices[9].nz=0.0f;
      pVertices[9].u =1.0f        ; pVertices[9].v =0.0f        ; pVertices[9].diffuse=0xFFFFFFFF;

      pVertices[10].nx=1.0f        ; pVertices[10].ny=0.0f        ; pVertices[10].nz=0.0f;
      pVertices[10].u =0.0f        ; pVertices[10].v =1.0f        ; pVertices[10].diffuse=0xFFFFFFFF;

      pVertices[11].nx=1.0f        ; pVertices[11].ny=0.0f        ; pVertices[11].nz=0.0f;
      pVertices[11].u =1.0f        ; pVertices[11].v =1.0f        ; pVertices[11].diffuse=0xFFFFFFFF;


      pVertices[12].nx=-1.0f        ; pVertices[12].ny=0.0f        ; pVertices[12].nz=0.0f;
      pVertices[12].u =0.0f        ; pVertices[12].v =0.0f        ; pVertices[12].diffuse=0xFFFFFFFF;

      pVertices[13].nx=-1.0f        ; pVertices[13].ny=0.0f        ; pVertices[13].nz=0.0f;
      pVertices[13].u =1.0f        ; pVertices[13].v =0.0f        ; pVertices[13].diffuse=0xFFFFFFFF;

      pVertices[14].nx=-1.0f        ; pVertices[14].ny=0.0f        ; pVertices[14].nz=0.0f;
      pVertices[14].u =0.0f        ; pVertices[14].v =1.0f        ; pVertices[14].diffuse=0xFFFFFFFF;

      pVertices[15].nx=-1.0f        ; pVertices[15].ny=0.0f        ; pVertices[15].nz=0.0f;
      pVertices[15].u =1.0f        ; pVertices[15].v =1.0f        ; pVertices[15].diffuse=0xFFFFFFFF;

      

      pVertices[16].nx=0.0f        ; pVertices[16].ny=1.0f        ; pVertices[16].nz=0.0f;
      pVertices[16].u =0.0f        ; pVertices[16].v =0.0f        ; pVertices[16].diffuse=0xFFFFFFFF;

      pVertices[17].nx=0.0f        ; pVertices[17].ny=1.0f        ; pVertices[17].nz=0.0f;
      pVertices[17].u =1.0f        ; pVertices[17].v =0.0f        ; pVertices[17].diffuse=0xFFFFFFFF;

      pVertices[18].nx=0.0f        ; pVertices[18].ny=1.0f        ; pVertices[18].nz=0.0f;
      pVertices[18].u =0.0f        ; pVertices[18].v =1.0f        ; pVertices[18].diffuse=0xFFFFFFFF;

      pVertices[19].nx=0.0f        ; pVertices[19].ny=1.0f        ; pVertices[19].nz=0.0f;
      pVertices[19].u =1.0f        ; pVertices[19].v =1.0f        ; pVertices[19].diffuse=0xFFFFFFFF;


      pVertices[20].nx=0.0f        ; pVertices[20].ny=-1.0f        ; pVertices[20].nz=0.0f;
      pVertices[20].u =0.0f        ; pVertices[20].v =0.0f        ; pVertices[20].diffuse=0xFFFFFFFF;

      pVertices[21].nx=0.0f        ; pVertices[21].ny=-1.0f        ; pVertices[21].nz=0.0f;
      pVertices[21].u =1.0f        ; pVertices[21].v =0.0f        ; pVertices[21].diffuse=0xFFFFFFFF;

      pVertices[22].nx=0.0f        ; pVertices[22].ny=-1.0f        ; pVertices[22].nz=0.0f;
      pVertices[22].u =0.0f        ; pVertices[22].v =1.0f        ; pVertices[22].diffuse=0xFFFFFFFF;

      pVertices[23].nx=0.0f        ; pVertices[23].ny=-1.0f        ; pVertices[23].nz=0.0f;
      pVertices[23].u =1.0f        ; pVertices[23].v =1.0f        ; pVertices[23].diffuse=0xFFFFFFFF;
    }

    /*
    pVertices[0].x =x        ; pVertices[0].y =y        ; pVertices[0].z=z; 
    pVertices[1].x =x+fGrain ; pVertices[1].y =y        ; pVertices[1].z=z;
    pVertices[2].x =x        ; pVertices[2].y =y+fGrain ; pVertices[2].z=z;
    pVertices[3].x =x+fGrain ; pVertices[3].y =y+fGrain ; pVertices[3].z=z;
    pVertices[4].x =x        ; pVertices[4].y =y        ; pVertices[4].z=z+fGrain;
    pVertices[5].x =x+fGrain ; pVertices[5].y =y        ; pVertices[5].z=z+fGrain;
    pVertices[6].x =x        ; pVertices[6].y =y+fGrain ; pVertices[6].z=z+fGrain;
    pVertices[7].x =x+fGrain ; pVertices[7].y =y+fGrain ; pVertices[7].z=z+fGrain;
    */
        

    unsigned short pusCanonicIndices[36]={  0, 3, 2,  // DOWN
					                                  0, 1, 3,  // DOWN

					                                  4, 7, 5,  // UP
					                                  4, 6, 7,  // UP

                                            8, 11, 10,  // LEFT
					                                  8, 9 , 11,  // LEFT

					                                  12, 14, 15,  // RIGHT
					                                  12, 15, 13,  // RIGHT

					                                  16, 18, 19,  // FRONT
					                                  16, 19, 17,  // FRONT

                                            20, 23, 22,  // BACK
					                                  20, 21, 23,  // BACK                                            
                                          };


    for (i = 0, pusIndices = g_pusEnvironmentIndices ; i < MAX_CUBES ; i++, pusIndices+=36)
    {
      int j;

      for (j = 0 ; j < 36 ; j++)
      {
        pusIndices[j] = pusCanonicIndices[j]+i*24;
      }
    }
  }
  
  int  Init()
  {
    g_uCurrent =0;
    
    InitFlatCubeBuffers();
    InitTexturedCubeBuffers();
    InitEnvironmentCubeBuffers();

    g_me.m_iShader            =ShaderManager::FindShader("NULL");
    g_me.m_iVB                =-1;
    g_me.m_iIB                =-1;
    g_me.m_pVertices          =0;
    g_me.m_pIndices           =0;
    g_me.m_uStartVertex       =0;
    g_me.m_uVertices          =0;
    g_me.m_uStartIndex        =0;
    g_me.m_uPrimitives        =0;
    g_me.m_WorldTransform     =Matrix4X4::Identity;
    g_me.m_iActiveLightMask   =-1;  
    g_me.m_fDepth             =0.0f;

    return 0;
  }

  int  Shutdown()
  {
    return 0;
  }    

  // Transforms meshelement verts to world
  void         TransformRange()
  {
    unsigned i;

    FVF_PosNormalDiffuseTex1* pSource = g_pME->m_pVertices;
    Vector3D*                 pTarget = Transformed;
    Matrix4X4* pM; 
    Matrix4X4 Out;


    if (g_pShader->m_uPasses && (g_pShader->m_pPasses[0].m_Flags & ShaderManager::E_MATRIXSCALE))
    {
      Matrix4X4 Scale;
      float fScale = g_pShader->m_pPasses[0].m_ScaleWave.Evaluate(Timer::GetTime());

      Scale.SetScale(fScale, fScale, fScale);

      Matrix4X4::Mult(&Out, &Scale, &g_pME->m_WorldTransform);
      pM = &Out;      
    }
    else
    {
      pM = &g_pME->m_WorldTransform;
    }

    if (g_bCreatingCache)
    {
      pM = &Matrix4X4::Identity;
    }
        
    for (i = 0 ; i<g_pME->m_uVertices ; i++)
    {
      float x=pSource[i].x;
      float y=pSource[i].y;
      float z=pSource[i].z;

      pTarget[i].x      = pM->m_11*x+pM->m_21*y+pM->m_31*z+pM->m_41;
      pTarget[i].y      = pM->m_12*x+pM->m_22*y+pM->m_32*z+pM->m_42;
      pTarget[i].z      = pM->m_13*x+pM->m_23*y+pM->m_33*z+pM->m_43;         
    }    
  }

  float Truncate(float f)
  {
    return (float(int(f)));
  }

  inline int fround(float input) 
  {
    int retCode;
    //float fHalf = 0.5;

    __asm   fld     input
	  __asm   frndint				//needed to solve some bugs
    //__asm   fsub  fHalf
    __asm   fistp	retCode

    return retCode;
  }

  template <class T>
  void swap(T &a, T &b) 
  {
	  T c;
	  c = a;
	  a = b;
	  b = c;
  }

  void         Flush()
  {

    g_me.m_uPrimitives = g_uCurrent*12;

    switch (g_RenderType)
    {
    case SOLIDFLAT:
      g_me.m_uVertices   = g_uCurrent*8;
      g_me.m_pVertices   = g_pVertices; 
      g_me.m_pIndices    = g_pusIndices;    
      break;
    case TEXTURE:
      g_me.m_uVertices   = g_uCurrent*12;
      g_me.m_pVertices   = g_pTexturedVertices; 
      g_me.m_pIndices    = g_pusTexturedIndices;          
      break;
    case ENVIRONMENT:
      g_me.m_uVertices   = g_uCurrent*24;
      g_me.m_pVertices   = g_pEnvironmentVertices; 
      g_me.m_pIndices    = g_pusEnvironmentIndices;                
      break;
    default:
      assert(!"Huh?");
      break;
    }
    

    if (g_bCreatingCache)
    {
      // Create resources
      int iVB = ResourceManager::CreateVertexBuffer(
                  g_me.m_uVertices*sizeof(FVF_PosNormalDiffuseTex1), 
                  D3DUSAGE_WRITEONLY, FVF_POSNORMALDIFFUSETEX1, 
                  D3DPOOL_MANAGED);

      int iIB = ResourceManager::CreateIndexBuffer(
                    g_me.m_uPrimitives*3*sizeof(unsigned short), 
                    D3DUSAGE_WRITEONLY , 
                    D3DFMT_INDEX16, 
                    D3DPOOL_MANAGED);


      if (iVB == -1 || iIB == -1)
      {
        SM_Main::OutputError("Failed to create voxel cache!");
        return ; 
      }


      FVF_PosNormalDiffuseTex1* pMemVertices;
      pMemVertices = new FVF_PosNormalDiffuseTex1[g_me.m_uVertices];

      unsigned short* pMemIndices;
      pMemIndices = new unsigned short[g_me.m_uPrimitives*3];

      FVF_PosNormalDiffuseTex1* pVertices;
      ResourceManager::GetVertexBufferFromID(iVB)->Lock(
        0, 
        sizeof(FVF_PosNormalDiffuseTex1)*g_me.m_uVertices, 
        (unsigned char**)&pVertices, 
        0);

      unsigned short* pIndices;
      ResourceManager::GetIndexBufferFromID(iIB)->Lock(
        0, 
        sizeof(unsigned short)*g_me.m_uPrimitives*3, 
        (unsigned char**)&pIndices, 
        0);

      
      memcpy(pVertices, g_me.m_pVertices, g_me.m_uVertices*sizeof(FVF_PosNormalDiffuseTex1));
      memcpy(pIndices, g_me.m_pIndices, sizeof(unsigned short)*g_me.m_uPrimitives*3);
      memcpy(pMemVertices, g_me.m_pVertices, g_me.m_uVertices*sizeof(FVF_PosNormalDiffuseTex1));
      memcpy(pMemIndices, g_me.m_pIndices, sizeof(unsigned short)*g_me.m_uPrimitives*3);

      ResourceManager::GetVertexBufferFromID(iVB)->Unlock();     
      ResourceManager::GetIndexBufferFromID(iIB)->Unlock();
      


      MeshElement* pME = new MeshElement;
      memcpy(pME, &g_me, sizeof(MeshElement));

      pME->m_pIndices = pMemIndices;
      pME->m_pVertices = pMemVertices;
      pME->m_iIB = iIB;
      pME->m_iVB = iVB;

      g_CacheArray.Add(pME);
    }
    else
    {
      // Hack. We flag out VOXELIZE so we can render cool
      if (g_me.m_uPrimitives)
      {
        MeshElement* p_me = &g_me;
        g_pShader->Render(&p_me, 1, true);
      }   
    }
       
    g_uCurrent = 0;    
    
  }


  void  set(int X1, int Y1, int Z1)
  {
    
    float x = float(X1); 
    float y = float(Y1); 
    float z = float(Z1); 

    switch (g_RenderType)
    {
    case SOLIDFLAT:
      {
        FVF_PosNormalDiffuseTex1* pVertices = g_pVertices + g_uCurrent*8;

        pVertices[0].x =x        ; pVertices[0].y =y        ; pVertices[0].z=z; 
        pVertices[1].x =x+fGrain ; pVertices[1].y =y        ; pVertices[1].z=z;
        pVertices[2].x =x        ; pVertices[2].y =y+fGrain ; pVertices[2].z=z;
        pVertices[3].x =x+fGrain ; pVertices[3].y =y+fGrain ; pVertices[3].z=z;
        pVertices[4].x =x        ; pVertices[4].y =y        ; pVertices[4].z=z+fGrain;
        pVertices[5].x =x+fGrain ; pVertices[5].y =y        ; pVertices[5].z=z+fGrain;
        pVertices[6].x =x        ; pVertices[6].y =y+fGrain ; pVertices[6].z=z+fGrain;
        pVertices[7].x =x+fGrain ; pVertices[7].y =y+fGrain ; pVertices[7].z=z+fGrain;
        break;
      }
    case TEXTURE:
      {
        FVF_PosNormalDiffuseTex1* pVertices = g_pTexturedVertices + g_uCurrent*12;

        pVertices[0].x =x          ; pVertices[0].y =y ; pVertices[0].z=z ; 
        pVertices[1].x =x+fGrain   ; pVertices[1].y =y ; pVertices[1].z=z ;
        pVertices[2].x =x          ; pVertices[2].y =y+fGrain  ; pVertices[2].z=z ;
        pVertices[3].x =x+fGrain           ; pVertices[3].y =y+fGrain  ; pVertices[3].z=z ;
        pVertices[4].x =x          ; pVertices[4].y =y ; pVertices[4].z=z+fGrain;
        pVertices[5].x =x+fGrain           ; pVertices[5].y =y ; pVertices[5].z=z+fGrain;
        pVertices[6].x =x          ; pVertices[6].y =y+fGrain  ; pVertices[6].z=z+fGrain;
        pVertices[7].x =x+fGrain           ; pVertices[7].y =y+fGrain  ; pVertices[7].z=z+fGrain;
        pVertices[8].x =x          ; pVertices[8].y =y          ; pVertices[8].z=z ; 
        pVertices[9].x =x+fGrain           ; pVertices[9].y =y          ; pVertices[9].z=z ;
        pVertices[10].x =x         ; pVertices[10].y =y+fGrain ; pVertices[10].z=z ;
        pVertices[11].x =x+fGrain          ; pVertices[11].y =y+fGrain ; pVertices[11].z=z ;
        break;
      }
    case ENVIRONMENT:
      {
        FVF_PosNormalDiffuseTex1* pVertices = g_pEnvironmentVertices + g_uCurrent*24;

        pVertices[0].x =x        ; pVertices[0].y =y          ; pVertices[0].z=z; 
        pVertices[1].x =x+fGrain ; pVertices[1].y =y          ; pVertices[1].z=z;
        pVertices[2].x =x        ; pVertices[2].y =y+fGrain   ; pVertices[2].z=z;
        pVertices[3].x =x+fGrain ; pVertices[3].y =y+fGrain   ; pVertices[3].z=z;

        pVertices[4].x =x        ; pVertices[4].y =y          ; pVertices[4].z=z+fGrain;
        pVertices[5].x =x+fGrain ; pVertices[5].y =y          ; pVertices[5].z=z+fGrain;
        pVertices[6].x =x        ; pVertices[6].y =y+fGrain   ; pVertices[6].z=z+fGrain;
        pVertices[7].x =x+fGrain ; pVertices[7].y =y+fGrain   ; pVertices[7].z=z+fGrain;
       
        pVertices[8].x  =x        ; pVertices[8].y =y        ; pVertices[8].z=z; 
        pVertices[9].x  =x        ; pVertices[9].y =y+fGrain ; pVertices[9].z=z;
        pVertices[10].x =x        ; pVertices[10].y=y        ; pVertices[10].z=z+fGrain;
        pVertices[11].x =x        ; pVertices[11].y=y+fGrain ; pVertices[11].z=z+fGrain;

        pVertices[12].x =x+fGrain ; pVertices[12].y =y        ; pVertices[12].z=z;
        pVertices[13].x =x+fGrain ; pVertices[13].y =y+fGrain ; pVertices[13].z=z;
        pVertices[14].x =x+fGrain ; pVertices[14].y =y        ; pVertices[14].z=z+fGrain;
        pVertices[15].x =x+fGrain ; pVertices[15].y =y+fGrain ; pVertices[15].z=z+fGrain;

        
        pVertices[16].x =x        ; pVertices[16].y =y        ; pVertices[16].z=z; 
        pVertices[17].x =x+fGrain ; pVertices[17].y =y        ; pVertices[17].z=z;
        pVertices[18].x =x        ; pVertices[18].y =y        ; pVertices[18].z=z+fGrain;
        pVertices[19].x =x+fGrain ; pVertices[19].y =y        ; pVertices[19].z=z+fGrain;

        pVertices[20].x =x        ; pVertices[20].y =y+fGrain ; pVertices[20].z=z;
        pVertices[21].x =x+fGrain ; pVertices[21].y =y+fGrain ; pVertices[21].z=z;
        pVertices[22].x =x        ; pVertices[22].y =y+fGrain ; pVertices[22].z=z+fGrain;
        pVertices[23].x =x+fGrain ; pVertices[23].y =y+fGrain ; pVertices[23].z=z+fGrain;        
      }
      break;
    }
    
    g_uCurrent++;        
    if (g_uCurrent == MAX_CUBES)
    {
      Flush();
    }
  }

    
  void drawLineCYDZ(float x1, int Y1, float z1, float x2, float dz) {
	int X1 = fround(x1);
	int X2 = fround(x2);
	z1 += ((X1+0.5f)-x1)*dz;
	
	assert((X2-X1) >= 0);
	while (X1 < X2) {
		set(X1++, Y1, (int)floor(z1));
		z1 += dz;
	}
}

//left
void drawLineCYDX(float x1, int Y1, float z1, float z2, float dx) {
	int Z1 = fround(z1);
	int Z2 = fround(z2);
	x1 += ((Z1+0.5f)-z1)*dx;

	assert((Z2-Z1) >= 0);
	while (Z1 < Z2) {
		set((int)floor(x1), Y1, Z1++);
		x1 += dx;
	}
}

//top
void drawLineCZDY(float x1, float y1, int Z1, float x2, float dy) {
	int X1 = fround(x1);
	int X2 = fround(x2);
	y1 += ((X1+0.5f)-x1)*dy;
	
	assert((X2-X1) >= 0);
	while (X1 < X2) {
		set(X1++, (int)floor(y1), Z1);
		y1 += dy;
	}
}


    void        VoxelizeTriKami()
    {
  Vector3D v1(*pTA);
  Vector3D v2(*pTB);
  Vector3D v3(*pTC);
  Vector3D v4;
    

	Vector3D vec1(v2-v1);
	Vector3D vec2(v2-v3);
  vec2 = Vector3D::Cross(vec2, vec1);
//	vec2.unit();

	vec2.x = (float)(float)fabs(vec2.x);
	vec2.y = (float)(float)fabs(vec2.y);
	vec2.z = (float)(float)fabs(vec2.z);

	int type;
	if ((vec2.z > vec2.x) && (vec2.z > vec2.y)) {
		type = 0; //front
	} else {
		if ((vec2.x > vec2.y) && (vec2.x > vec2.z)) {
			type = 1; //left
			swap(v1.x, v1.z);
			swap(v2.x, v2.z);
			swap(v3.x, v3.z);
		} else {
			type = 2; //top
			swap(v1.y, v1.z);
			swap(v2.y, v2.z);
			swap(v3.y, v3.z);
		}
	}

	if (v2.y < v1.y) {
		swap(v1.x, v2.x);
		swap(v1.y, v2.y);
		swap(v1.z, v2.z);
	}
	if (v3.y < v2.y) {
		swap(v2.x, v3.x);
		swap(v2.y, v3.y);
		swap(v2.z, v3.z);
	}
	if (v2.y < v1.y) {
		swap(v1.x, v2.x);
		swap(v1.y, v2.y);
		swap(v1.z, v2.z);
	}

	int Y = fround(v1.y);
	int MidY = fround(v2.y);
	int MaxY = fround(v3.y);

	if (Y == MaxY) {
		return;
	}

	v4.y = v2.y;
	v4.x = v1.x + (v3.x-v1.x) * (float)fabs((v2.y-v1.y)/(v3.y-v1.y));
	v4.z = v1.z + (v3.z-v1.z) * (float)fabs((v2.y-v1.y)/(v3.y-v1.y));

	if (v2.x > v4.x) {
		swap(v2.x, v4.x);
		swap(v2.y, v4.y);
		swap(v2.z, v4.z);
	}

	float dz = (v4.z - v2.z) / (v4.x - v2.x);

	float dxl, dxr, dzl;
	float xl, xr, zl;
	float iy;

	if (Y != MidY) {
		dxl = v2.x-v1.x;
		dzl = v2.z-v1.z;
		dxr = v4.x-v1.x;

		iy = 1.0f/(float)fabs(v2.y-v1.y);
		dxl *= iy;
		dzl *= iy;
		dxr *= iy;

		xl = v1.x + (float)fabs((Y+0.5f)-(v1.y))*dxl;
		zl = v1.z + (float)fabs((Y+0.5f)-(v1.y))*dzl;		
		xr = v1.x + (float)fabs((Y+0.5f)-(v1.y))*dxr;
	}

	while (Y < MaxY) {
//		if (Y >= height) {
//			return;
//		}

		if (Y == MidY) {
			dxl = v3.x-v2.x;
			dzl = v3.z-v2.z;
			dxr = v3.x-v4.x;

			iy = 1.0f/(float)fabs(v3.y-v2.y);
			dxl *= iy;
			dzl *= iy;
			dxr *= iy;

			xl = v2.x + (float)fabs((Y+0.5f)-(v2.y))*dxl;
			zl = v2.z + (float)fabs((Y+0.5f)-(v2.y))*dzl;
			xr = v4.x + (float)fabs((Y+0.5f)-(v2.y))*dxr;
		}

//		if (Y >= 0) {
			switch (type) {
			case 0:
				drawLineCYDZ(xl, Y, zl, xr, dz);
				break;
			case 1:
				drawLineCYDX(zl, Y, xl, xr, dz);
				break;
			case 2:
				drawLineCZDY(xl, zl, Y, xr, dz);
				break;
			}
//		}

		Y++;
		xl += dxl;
		zl += dzl;
		xr += dxr;
	}
}

    void        VoxelizeTri()
    { 
      if (g_bCreatingCache)
      {
      }
      else
      if ((g_pShader->m_Flags & ShaderManager::SH_VOXELIZE_NOCULL) == 0)
      {
        Vector3D Normal = Vector3D::Cross(*pTC-*pTA, *pTB-*pTA);
        if (Vector3D::Dot(SM_Voxelizer::g_pRC->GetViewport()->m_v3dPosition-*pTA, Normal) < 0.0)
        {
          return;
        }
      }
           
      VoxelizeTriKami();      
    }

    void    CacheMeshElement(MeshElement* pME)
    {
      g_pME = pME;
      g_me.m_iShader = pME->m_iShader;
      g_me.m_WorldTransform = Matrix4X4::Identity;
      g_pShader = ShaderManager::GetShader(g_me.m_iShader);

      g_pShader = ShaderManager::GetShader(g_me.m_iShader);
      if (!(g_pShader->m_Flags & ShaderManager::SH_VOXELIZE_STATIC))
      {
        return;
      }

      if (pME->m_pCache)
      {
        delete pME->m_pCache;
      }

      g_bCreatingCache = true;
      g_CacheArray.Reset();
            
      if (g_pShader->UsesTexGen())
      {
        g_RenderType = ENVIRONMENT;
      }
      else if (g_pShader->UsesTextures())
      {
        g_RenderType = TEXTURE;
      }
      else
      {
        g_RenderType = SOLIDFLAT;
      }
    
      g_pShader->m_Flags &= ~ShaderManager::SH_VOXELIZE;
             
      // Transform source vertices
      SM_Voxelizer::TransformRange();

      bool bScale = false;
      if (g_pShader->m_uPasses && (g_pShader->m_pPasses[0].m_Flags & ShaderManager::E_MATRIXSCALE))
      {
        g_pShader->m_pPasses[0].m_Flags &= ~ShaderManager::E_MATRIXSCALE;
        bScale = true;
      }
    
      unsigned uIndices = pME->m_uPrimitives*3;

      unsigned i;
      for (i = 0 ; i < uIndices ; i += 3)
      {
        // Fill out state
        Vector3D v1 = Transformed[pME->m_pIndices[i]];
        Vector3D v2 = Transformed[pME->m_pIndices[i+1]];
        Vector3D v3 = Transformed[pME->m_pIndices[i+2]];

        pTA = &v1;
        pTB = &v2;
        pTC = &v3;

        pVA = &pME->m_pVertices[pME->m_pIndices[i]];
        pVB = &pME->m_pVertices[pME->m_pIndices[i+1]];
        pVC = &pME->m_pVertices[pME->m_pIndices[i+2]];

        VoxelizeTri();        
      }

      // Flush
      Flush();            

      g_pShader->m_Flags |= ShaderManager::SH_VOXELIZE;

      if (bScale)
      {
        g_pShader->m_pPasses[0].m_Flags |= ShaderManager::E_MATRIXSCALE;
      }

      
      // Ok, now commit what we have stored to the cache.
      VoxelizerCache* pCache = new VoxelizerCache();
      pCache->m_uMeshElements = g_CacheArray.Used();
      pCache->m_ppMeshElements = new MeshElement*[pCache->m_uMeshElements];
      memcpy(pCache->m_ppMeshElements, g_CacheArray.Get(), sizeof(MeshElement*)*pCache->m_uMeshElements);        

      pME->m_pCache = pCache;
      g_bCreatingCache = false;        
    }

    void    DrawMeshElement(MeshElement* pME, RenderContext* pRenderContext)
    {
      SM_Voxelizer::g_pME = pME;
      SM_Voxelizer::g_pRC = pRenderContext;

      unsigned i;

      
      // Set shader
      g_me.m_iShader = pME->m_iShader;
      g_me.m_WorldTransform = Matrix4X4::Identity;
      g_pShader = ShaderManager::GetShader(g_me.m_iShader);

      
      if (SM_Voxelizer::g_pME->m_uVertices >= MAX_VERTS)
      {
        static bool bGivenError = false;
        if (!bGivenError)
        {
          SM_Main::OutputError("MAX_VERTS reached. The Mesh wont be rendered. Contact mac to get limit increased or use smaller meshes\n");
          bGivenError = true;
        }

        return;
      }

      if (!pME->m_pCache)
      {
        if (g_pShader->m_Flags & ShaderManager::SH_VOXELIZE_STATIC)
        {
        CacheMeshElement(pME);
        }
        else
        {        
          if (g_pShader->UsesTexGen())
          {
            g_RenderType = ENVIRONMENT;
          }
          else if (g_pShader->UsesTextures())
          {
            g_RenderType = TEXTURE;
          }
          else
          {
            g_RenderType = SOLIDFLAT;
          }

      
          g_pShader->m_Flags &= ~ShaderManager::SH_VOXELIZE;
               
          // Transform source vertices
          SM_Voxelizer::TransformRange();

          bool bScale = false;
          if (g_pShader->m_uPasses && (g_pShader->m_pPasses[0].m_Flags & ShaderManager::E_MATRIXSCALE))
          {
            g_pShader->m_pPasses[0].m_Flags &= ~ShaderManager::E_MATRIXSCALE;
            bScale = true;
          }
      
          unsigned uIndices = pME->m_uPrimitives*3;
      
          for (i = 0 ; i < uIndices ; i += 3)
          {
            // Fill out state
            Vector3D v1 = Transformed[pME->m_pIndices[i]];
            Vector3D v2 = Transformed[pME->m_pIndices[i+1]];
            Vector3D v3 = Transformed[pME->m_pIndices[i+2]];

            pTA = &v1;
            pTB = &v2;
            pTC = &v3;

            pVA = &pME->m_pVertices[pME->m_pIndices[i]];
            pVB = &pME->m_pVertices[pME->m_pIndices[i+1]];
            pVC = &pME->m_pVertices[pME->m_pIndices[i+2]];

            VoxelizeTri();        
          }

          // Flush
          Flush();            

          g_pShader->m_Flags |= ShaderManager::SH_VOXELIZE;

          if (bScale)
          {
            g_pShader->m_pPasses[0].m_Flags |= ShaderManager::E_MATRIXSCALE;
          }        
        }
      }
      else
      {
        // Use cached geometry
        VoxelizerCache* pCache = (VoxelizerCache*)pME->m_pCache;

        for (unsigned i = 0 ; i< pCache->m_uMeshElements ; i++)
        {
          pCache->m_ppMeshElements[i]->m_WorldTransform = pME->m_WorldTransform;
        }

        pME->m_pCache->Render(g_pRC, -1);
      }
    }
}

/*
void drawLineCYDZ(float x1, int Y1, float z1, float x2, float dz) {
	int X1 = fround(x1);
	int X2 = fround(x2);
	z1 += ((X1+0.5f)-x1)*dz;
	
	assert((X2-X1) >= 0);
	while (X1 < X2) {
		set(X1++, Y1, (int)floor(z1));
		z1 += dz;
	}
}

//left
void drawLineCYDX(float x1, int Y1, float z1, float z2, float dx) {
	int Z1 = fround(z1);
	int Z2 = fround(z2);
	x1 += ((Z1+0.5f)-z1)*dx;

	assert((Z2-Z1) >= 0);
	while (Z1 < Z2) {
		set((int)floor(x1), Y1, Z1++);
		x1 += dx;
	}
}

//top
void drawLineCZDY(float x1, float y1, int Z1, float x2, float dy) {
	int X1 = fround(x1);
	int X2 = fround(x2);
	y1 += ((X1+0.5f)-x1)*dy;
	
	assert((X2-X1) >= 0);
	while (X1 < X2) {
		set(X1++, (int)floor(y1), Z1);
		y1 += dy;
	}
}



void        VoxelizeTriKami()
{
  Vector3D v1(*pTA);
  Vector3D v2(*pTB);
  Vector3D v3(*pTC);
  Vector3D v4;
    

	Vector3D vec1(v2-v1);
	Vector3D vec2(v2-v3);
  vec2 = Vector3D::Cross(vec2, vec1);
//	vec2.unit();

	vec2.x = (float)(float)fabs(vec2.x);
	vec2.y = (float)(float)fabs(vec2.y);
	vec2.z = (float)(float)fabs(vec2.z);

	int type;
	if ((vec2.z > vec2.x) && (vec2.z > vec2.y)) {
		type = 0; //front
	} else {
		if ((vec2.x > vec2.y) && (vec2.x > vec2.z)) {
			type = 1; //left
			swap(v1.x, v1.z);
			swap(v2.x, v2.z);
			swap(v3.x, v3.z);
		} else {
			type = 2; //top
			swap(v1.y, v1.z);
			swap(v2.y, v2.z);
			swap(v3.y, v3.z);
		}
	}

	if (v2.y < v1.y) {
		swap(v1.x, v2.x);
		swap(v1.y, v2.y);
		swap(v1.z, v2.z);
	}
	if (v3.y < v2.y) {
		swap(v2.x, v3.x);
		swap(v2.y, v3.y);
		swap(v2.z, v3.z);
	}
	if (v2.y < v1.y) {
		swap(v1.x, v2.x);
		swap(v1.y, v2.y);
		swap(v1.z, v2.z);
	}

	int Y = fround(v1.y);
	int MidY = fround(v2.y);
	int MaxY = fround(v3.y);

	if (Y == MaxY) {
		return;
	}

	v4.y = v2.y;
	v4.x = v1.x + (v3.x-v1.x) * (float)fabs((v2.y-v1.y)/(v3.y-v1.y));
	v4.z = v1.z + (v3.z-v1.z) * (float)fabs((v2.y-v1.y)/(v3.y-v1.y));

	if (v2.x > v4.x) {
		swap(v2.x, v4.x);
		swap(v2.y, v4.y);
		swap(v2.z, v4.z);
	}

	float dz = (v4.z - v2.z) / (v4.x - v2.x);

	float dxl, dxr, dzl;
	float xl, xr, zl;
	float iy;

	if (Y != MidY) {
		dxl = v2.x-v1.x;
		dzl = v2.z-v1.z;
		dxr = v4.x-v1.x;

		iy = 1.0f/(float)fabs(v2.y-v1.y);
		dxl *= iy;
		dzl *= iy;
		dxr *= iy;

		xl = v1.x + (float)fabs((Y+0.5f)-(v1.y))*dxl;
		zl = v1.z + (float)fabs((Y+0.5f)-(v1.y))*dzl;		
		xr = v1.x + (float)fabs((Y+0.5f)-(v1.y))*dxr;
	}

	while (Y < MaxY) {
//		if (Y >= height) {
//			return;
//		}

		if (Y == MidY) {
			dxl = v3.x-v2.x;
			dzl = v3.z-v2.z;
			dxr = v3.x-v4.x;

			iy = 1.0f/(float)fabs(v3.y-v2.y);
			dxl *= iy;
			dzl *= iy;
			dxr *= iy;

			xl = v2.x + (float)fabs((Y+0.5f)-(v2.y))*dxl;
			zl = v2.z + (float)fabs((Y+0.5f)-(v2.y))*dzl;
			xr = v4.x + (float)fabs((Y+0.5f)-(v2.y))*dxr;
		}

//		if (Y >= 0) {
			switch (type) {
			case 0:
				drawLineCYDZ(xl, Y, zl, xr, dz);
				break;
			case 1:
				drawLineCYDX(zl, Y, xl, xr, dz);
				break;
			case 2:
				drawLineCZDY(xl, zl, Y, xr, dz);
				break;
			}
//		}

		Y++;
		xl += dxl;
		zl += dzl;
		xr += dxr;
	}
}
*/