#include <stdlib.h>
#include "SM_Engine3DPCH.h"
#include "SM_Shader.h"
#include "MBStaticList.h"
#include "MSystemFunctions.h"
#include "SM_MeshElement.h"
#include "SM_Voxelizer.h"
#include "MBinArray.h"
#include "ML.h"
#include "SM_VertexShader.h"

#define MMATH_PI 3.1415926535897932384626433832795

bool g_bWireframe = false;

void  DoVertices(Matrix4X4* pM, FVF_PosNormalDiffuseTex1* pTarget, FVF_PosNormalDiffuseTex1* pSource, unsigned uVertices)
{
  unsigned i;

  for (i=0 ; i<uVertices ; ++i)
  {
    /*
    float oow=1.0f/(m_14*pS[i].x+m_24*pS[i].y+m_34*pS[i].z+m_44);
    pD[i].x=(m_11*pS[i].x+m_21*pS[i].y+m_31*pS[i].z+m_41 )*oow;
    pD[i].y=(m_12*pS[i].x+m_22*pS[i].y+m_32*pS[i].z+m_42 )*oow;
    pD[i].z=(m_13*pS[i].x+m_23*pS[i].y+m_33*pS[i].z+m_43 )*oow;    
    */
    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;
    pTarget[i].nx     = pM->m_11*pSource[i].nx+pM->m_21*pSource[i].ny+pM->m_31*pSource[i].nz;
    pTarget[i].ny     = pM->m_12*pSource[i].nx+pM->m_22*pSource[i].ny+pM->m_32*pSource[i].nz;
    pTarget[i].nz     = pM->m_13*pSource[i].nx+pM->m_23*pSource[i].ny+pM->m_33*pSource[i].nz;
    pTarget[i].diffuse= pSource[i].diffuse;
    pTarget[i].u      = pSource[i].u;
    pTarget[i].v      = pSource[i].v;

  }
}

void  DoDistort(Matrix4X4* pM, FVF_PosNormalDiffuseTex1* pTarget, FVF_PosNormalDiffuseTex1* pSource, unsigned uVertices)
{
  unsigned i;

  for (i=0 ; i<uVertices ; ++i)
  {
    /*
    float oow=1.0f/(m_14*pS[i].x+m_24*pS[i].y+m_34*pS[i].z+m_44);
    pD[i].x=(m_11*pS[i].x+m_21*pS[i].y+m_31*pS[i].z+m_41 )*oow;
    pD[i].y=(m_12*pS[i].x+m_22*pS[i].y+m_32*pS[i].z+m_42 )*oow;
    pD[i].z=(m_13*pS[i].x+m_23*pS[i].y+m_33*pS[i].z+m_43 )*oow;    
    */
    float x=pSource[i].x;
    float y=pSource[i].y;
    float z=pSource[i].z;

    //pSource[i]

    float fTime=2.0f*Timer::GetTime();
    //Vector3D v3d(cosf(2.0f*fTime+2*x)*sinf(fTime+2*z)*0.5f, 5.0, sinf(fTime+2*z)*cosf(fTime+x+z)*0.5f);
    Vector3D v3d(cosf(2.0f*fTime+3*i)*sinf(fTime+5*i)*0.5f, 5.0, sinf(fTime+i)*cosf(fTime+7*i)*0.5f);
    v3d.Normalize();

    
    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;
    pTarget[i].nx     = pM->m_11*v3d.x+pM->m_21*v3d.y+pM->m_31*v3d.z;
    pTarget[i].ny     = pM->m_12*v3d.x+pM->m_22*v3d.y+pM->m_32*v3d.z;
    pTarget[i].nz     = pM->m_13*v3d.x+pM->m_23*v3d.y+pM->m_33*v3d.z;
    pTarget[i].diffuse= 0xFFFFFFFF;
    //pTarget[i].u      = pSource[i].u;
    //pTarget[i].v      = v3d.y; //pSource[i].v;

  }
}



namespace ShaderManager
{

bool g_b32BitTextures=false;

// Helpers
;

int                m_iPolygons = 0;
int                m_iNullShader=-1;
bool               m_bBlendOverride = false;

bool               m_bTFactorOverride = false;
unsigned           m_uTFactorOverride= 0;

void    OverrideTFactor  (bool bOverride, unsigned uColor)
{
  m_bTFactorOverride = bOverride;
  m_uTFactorOverride = uColor;
}

void    OverrideBlend    (bool bBlend)
{
  m_bBlendOverride = bBlend;
}

void NormalizeName(char* pcNormalized, const char* pcSourceName)
{
  MExtractBasename(pcNormalized, pcSourceName);
  strupr(pcNormalized);
}  
RenderContext* g_pRC=0;

void   SetRenderContext(RenderContext* pRC)
{
  g_pRC=pRC;
}




class Texture
{
public:
  Texture()
  {
    m_iD3DTextureID =-1;
    m_pcTextureName =0;
    m_iReferences   =0;
  }

  ~Texture()
  {
    Shutdown();
  }

  int     Init(const char* pcTextureName, int iD3DTextureID)
  {
    m_iD3DTextureID=iD3DTextureID;
    m_pcTextureName=new char[strlen(pcTextureName)+1];
    if (!m_pcTextureName)
    {
      return (-1);
    }
    m_iReferences  =1;

    return (0);
  }

  int     Shutdown()
  {
    if (m_pcTextureName) 
    {
      delete[] m_pcTextureName;
      m_pcTextureName=0;
    }

    if (m_iD3DTextureID!=-1) 
    {
      ResourceManager::ReleaseTexture(m_iD3DTextureID);
      m_iD3DTextureID=-1;
    }

    m_iReferences=0;

    return (0);
  }

  char*   m_pcTextureName;
  int     m_iD3DTextureID;
  int     m_iReferences;
};

float WaveForm::Evaluate(float fTime)
{
  float b,p,f,t,a;
  float fScaledTime;

  a=m_fAmplitude;
  b=m_fBase;
  f=m_fFrequency;
  p=m_fOffset;
  t=fTime;

  switch (m_Type)
  {      

  case WF_CONSTANT:
    return (b);
  case WF_SIN:
    fScaledTime=f*t;
    fScaledTime-=float(floor(fScaledTime));
    fScaledTime+=p;
    fScaledTime-=float(floor(fScaledTime));
    return (b+a*float(sin(2.0f*MMATH_PI*fScaledTime)));
  case WF_TRIANGLE:
    fScaledTime=f*t;
    fScaledTime-=float(floor(fScaledTime));
    fScaledTime+=p;
    fScaledTime-=float(floor(fScaledTime));  
    return (b+a*(fScaledTime<0.5f?2.0f*fScaledTime:1.0f-2.0f*(fScaledTime-0.5f)));
  case WF_SQUARE:
    fScaledTime=f*t;
    fScaledTime-=float(floor(fScaledTime));
    fScaledTime+=p;
    fScaledTime-=float(floor(fScaledTime));  
    return (b+a*(fScaledTime>0.5f?-1.0f:1.0f));
  case WF_SAWTOOTH:
    fScaledTime=f*t;
    fScaledTime-=float(floor(fScaledTime));
    fScaledTime+=p;
    fScaledTime-=float(floor(fScaledTime)); 
    return (b+a*(fScaledTime));
  case WF_INVSAWTOOTH:
    fScaledTime=f*t;
    fScaledTime-=float(floor(fScaledTime));
    fScaledTime+=p;
    fScaledTime-=float(floor(fScaledTime));  
    return (b+a*(1.0f-fScaledTime));
  }

  assert(!"Incorrect Waveform");
  return 0.0f;
}


ShaderPass::ShaderPass()
{
m_CullMode      =CULL_CW;  
m_FillMode      =RM_SOLID;
m_ShadeMode     =SM_GOURAUD;
m_MipFilter     =MIP_LINEAR;
m_ZComp         =ZC_LESSEQUAL;
m_Material.Diffuse.r=m_Material.Diffuse.g=m_Material.Diffuse.b=m_Material.Diffuse.a=1.0f;
m_Material.Ambient.r=m_Material.Ambient.g=m_Material.Ambient.b=m_Material.Ambient.a=1.0f;
m_Material.Specular.r=m_Material.Specular.g=m_Material.Specular.b=m_Material.Specular.a=1.0f;
m_Material.Emissive.r=m_Material.Emissive.g=m_Material.Emissive.b=m_Material.Emissive.a=0.0f;
m_Material.Power=0.0f;
m_uTFactor      =0;
}

bool ShaderPass::UsesTextures()
{
  return (
    m_ColorArg1 == PA_TEXTURE ||
    m_ColorArg2 == PA_TEXTURE ||
    m_AlphaArg1 == PA_TEXTURE ||
    m_AlphaArg2 == PA_TEXTURE);    
}

bool ShaderPass::UsesTexGen()
{
  return m_TexGen != TG_PASSTHRU;
}


Shader::Shader()
{
  m_uPriority      =PR_OPAQUE;
  m_uPasses        =0;
  m_pPasses        =0;
  m_PosGen         =PG_PASSTHRU;   
  m_NormalGen      =NG_PASSTHRU;
  m_pcShaderName   =0;
  m_iReferences    =1;                  
  m_pVertexShader  =0;
  m_Flags          =0;
  m_bReceiveShadows=false;
  m_bProjectShadows=false;
  m_bProjectBeams  =false;
}

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

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

int Shader::Shutdown()
{
  unsigned i;

  for (i=0 ; i<m_uPasses ; i++)
  {
    if (m_pPasses[i].m_pcTextureName)
    {
      delete[] m_pPasses[i].m_pcTextureName;
      m_pPasses[i].m_pcTextureName=0;
    }              
  }

  ReleaseResources();

  if (m_pPasses) delete[] m_pPasses;
  m_pPasses=0;

  if (m_pcShaderName) delete[] m_pcShaderName;
  m_pcShaderName=0;    

  return (0);
}


int Shader::LoadResources()
{
  unsigned i;
  for (i=0 ; i<m_uPasses ; i++)
  { 
    if (m_pPasses[i].m_pcTextureName)
    {
      if ((m_pPasses[i].m_iTextureID=LoadTexture(m_pPasses[i].m_pcTextureName))==-1)
      {
        goto FAIL;
      }        
    }      
  }

  return 0;

FAIL:
  ReleaseResources();
  return -1;
}

int Shader::ReleaseResources()
{
  unsigned i;
  for (i=0 ; i<m_uPasses ; i++)
  { 
    if (m_pPasses[i].m_iTextureID!=-1)
    {
      ReleaseTexture(m_pPasses[i].m_iTextureID);
      m_pPasses[i].m_iTextureID=-1;
    }      
  }

  return 0;
}

int Shader::ReloadResources()
{
	ReleaseResources();
	return LoadResources();
}

int Shader::Release()
{
  assert(m_iReferences>=0);

  m_iReferences--;

  if (m_iReferences==0)
  {
    ReleaseResources();
  }    

  return m_iReferences;
}


int Shader::AddRef()
{
  if (m_iReferences==0)
  {
    if (LoadResources()==-1)
    {
      return m_iReferences;
    }
  }

  m_iReferences++;

  
  
  return m_iReferences;
}



int  Shader::SetShaderState(int iPass)
{
  SM_D3d::Device()->SetMaterial(&m_pPasses[iPass].m_Material);
  SM_D3d::SetRenderState(D3DRS_SPECULARENABLE , m_pPasses[iPass].m_Material.Power?TRUE:FALSE);
  SM_D3d::SetRenderState(D3DRS_LOCALVIEWER , m_pPasses[iPass].m_Material.Power?TRUE:FALSE ||
                                             m_pPasses[iPass].m_TexGen == TG_CAMERAREFLECTION ||
                                             m_pPasses[iPass].m_TexGen == TG_CUBEREFLECTION
                                             );

  
         
  
  SM_D3d::Device()->SetTexture(0, GetD3DTextureFromID(m_pPasses[iPass].m_iTextureID));    
  

  // Blending
  if (m_bBlendOverride)
  {
    SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);                
    SM_D3d::SetRenderState(D3DRS_SRCBLEND, BL_ONE);
    SM_D3d::SetRenderState(D3DRS_DESTBLEND, BL_ONE);  
  }
  else
  {
    if (m_pPasses[iPass].m_SrcBlend==BL_ONE && m_pPasses[iPass].m_DstBlend==BL_ZERO)
    {
      // The driver should catch this... but everybody knows how some drivers are....
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);                
    }
    else
    {
      SM_D3d::SetRenderState(D3DRS_ALPHABLENDENABLE, TRUE);                
      SM_D3d::SetRenderState(D3DRS_SRCBLEND, m_pPasses[iPass].m_SrcBlend);
      SM_D3d::SetRenderState(D3DRS_DESTBLEND, m_pPasses[iPass].m_DstBlend);  
  
    }
  }

  


  if (m_pPasses[iPass].m_Flags&E_NOZWRITE)
  {
    SM_D3d::SetRenderState(D3DRS_ZWRITEENABLE, FALSE);
  }
  else
  {
    SM_D3d::SetRenderState(D3DRS_ZWRITEENABLE, TRUE);        
  }

  SM_D3d::SetRenderState(D3DRS_SHADEMODE, m_pPasses[iPass].m_ShadeMode);  
  
  SM_D3d::SetRenderState(D3DRS_ZFUNC,    m_pPasses[iPass].m_ZComp);
  SM_D3d::SetRenderState(D3DRS_CULLMODE, m_pPasses[iPass].m_CullMode);
  SM_D3d::SetRenderState(D3DRS_FILLMODE, m_pPasses[iPass].m_FillMode);
  
  if (g_bWireframe)
  {
    SM_D3d::SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
  }
  //SM_D3d::SetRenderState(D3DRS_FILLMODE, D3DFILL_WIREFRAME);
  //SM_D3d::SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
  
  SM_D3d::SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
  
  


  // Pipeline
  SM_D3d::SetTextureStageState(0, D3DTSS_COLOROP   , m_pPasses[iPass].m_ColorOp);
  

  if (m_pPasses[iPass].m_ColorOp == PO_DOT3)
  {
    // Quirk with alphatest. Dot3 replicates to alpha and seems to override stage settings :(
    SM_D3d::SetRenderState(D3DRS_ALPHATESTENABLE,          FALSE);
  }
  else
  {
    SM_D3d::SetRenderState(D3DRS_ALPHATESTENABLE,          TRUE);
  }

  SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG1 , m_pPasses[iPass].m_ColorArg1);
  SM_D3d::SetTextureStageState(0, D3DTSS_COLORARG2 , m_pPasses[iPass].m_ColorArg2);
  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAOP   , m_pPasses[iPass].m_AlphaOp);
  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG1 , m_pPasses[iPass].m_AlphaArg1);
  SM_D3d::SetTextureStageState(0, D3DTSS_ALPHAARG2 , m_pPasses[iPass].m_AlphaArg2);
  SM_D3d::SetTextureStageState(0, D3DTSS_MIPFILTER,  m_pPasses[iPass].m_MipFilter);

  switch (m_pPasses[iPass].m_TexGen)
  {
  case TG_SPHEREREFLECTION:        
    {
    Matrix4X4 tm;
    tm.m_11=0.5f; tm.m_12= 0.0f; tm.m_13=0.0f; tm.m_14=0.0f; 
    tm.m_21=0.0f; tm.m_22=-0.5f; tm.m_23=0.0f; tm.m_24=0.0f; 
    tm.m_31=0.0f; tm.m_32= 0.0f; tm.m_33=1.0f; tm.m_34=0.0f; 
    tm.m_41=0.5f; tm.m_42= 0.5f; tm.m_43=0.0f; tm.m_44=1.0f; 

    SM_D3d::Device()->SetTransform(D3DTS_TEXTURE0 , (D3DMATRIX*) &tm);                        
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );        
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACENORMAL);          
    }
    break;

  case TG_CAMERAREFLECTION:
    {
    Matrix4X4 tm;
    tm.m_11=0.5f; tm.m_12= 0.0f; tm.m_13=0.0f; tm.m_14=0.0f; 
    tm.m_21=0.0f; tm.m_22=-0.5f; tm.m_23=0.0f; tm.m_24=0.0f; 
    tm.m_31=0.0f; tm.m_32= 0.0f; tm.m_33=1.0f; tm.m_34=0.0f; 
    tm.m_41=0.5f; tm.m_42= 0.5f; tm.m_43=0.0f; tm.m_44=1.0f; 

    SM_D3d::Device()->SetTransform(D3DTS_TEXTURE0 , (D3DMATRIX*) &tm);                        
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT2 );        
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);
    }
  case TG_CUBEREFLECTION:
    {
      /*
      Matrix4X4 tm2;

      g_pRC->GetViewport()->InverseViewMatrix(tm2);

      tm2.m_41=0.0f; tm2.m_42= 0.0f; tm2.m_43=0.0f; tm2.m_44=1.0f; 
      
      Matrix4X4 tm;
      tm.m_11=0.5f; tm.m_12= 0.0f; tm.m_13=0.0f; tm.m_14=0.0f; 
      tm.m_21=0.0f; tm.m_22=-0.5f; tm.m_23=0.0f; tm.m_24=0.0f; 
      tm.m_31=0.0f; tm.m_32= 0.0f; tm.m_33=1.0f; tm.m_34=0.0f; 
      tm.m_41=0.5f; tm.m_42= 0.5f; tm.m_43=0.0f; tm.m_44=1.0f; 

      Matrix4X4 tmDest;
      Matrix4X4::Mult(&tmDest, &tm2, &tm);


      SM_D3d::Device()->SetTransform(D3DTS_TEXTURE0 , (D3DMATRIX*) &tmDest);                        
      */
      /*
      Matrix4X4 tm2;
      g_pRC->GetViewport()->InverseViewMatrix(tm2);
      tm2.m_41=0.0f; tm2.m_42= 0.0f; tm2.m_43=0.0f; tm2.m_44=1.0f; 
      */
      
      Matrix4X4 tm;
      
      g_pRC->GetViewport()->InverseViewMatrix(tm);
      tm.m_41=0.0f; tm.m_42= 0.0f; tm.m_43=0.0f; tm.m_44=1.0f; 

      SM_D3d::Device()->SetTransform(D3DTS_TEXTURE0,  (D3DMATRIX*) &tm);                            
      SM_D3d::SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_COUNT3);
      SM_D3d::SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_CAMERASPACEREFLECTIONVECTOR);      
    }
        
    break;
  default:
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXCOORDINDEX, D3DTSS_TCI_PASSTHRU); 
    SM_D3d::SetTextureStageState( 0, D3DTSS_TEXTURETRANSFORMFLAGS, D3DTTFF_DISABLE );        
    break;
  }

  unsigned uTFactor=m_pPasses[iPass].m_uTFactor;

  if (m_bTFactorOverride)
  {
    uTFactor = m_uTFactorOverride;
  }
  else
  {    
    if (m_pPasses[iPass].m_Flags & E_ALPHAWAVE)
    {
      uTFactor|=unsigned(float(m_pPasses[iPass].m_AlphaWave.Evaluate(Timer::GetTime()))*255.0f)<<24;
    }
    else
    {
      uTFactor|=0xFF000000;
    }

    if (m_pPasses[iPass].m_Flags & E_COLORWAVE)
    {
      unsigned uColor=unsigned(float(m_pPasses[iPass].m_ColorWave.Evaluate(Timer::GetTime()))*255.0f);
      uTFactor|=(uColor<<16) | (uColor<<8) | uColor;
    }
        
  }

  if (m_pPasses[iPass].m_Flags & E_SPECULARADD)
  {
    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  );      
  }
  else
  {
    SM_D3d::SetTextureStageState(1, D3DTSS_COLOROP   , D3DTOP_DISABLE);
    SM_D3d::SetTextureStageState(1, D3DTSS_ALPHAOP   , D3DTOP_DISABLE);
  }
  
  SM_D3d::SetRenderState(D3DRS_TEXTUREFACTOR, uTFactor);
  
  
  SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSU, D3DTADDRESS_WRAP);
  SM_D3d::SetTextureStageState(0, D3DTSS_ADDRESSV, D3DTADDRESS_WRAP);
  

  return 0;
}

bool Shader::UsesTextures()
{
  unsigned i;

  for (i = 0 ; i < m_uPasses ; i++)
  {
    if (m_pPasses[i].UsesTextures())
    {
      return true;
    }
  }

  return false;
}

bool Shader::UsesTexGen()
{
  unsigned i;

  for (i = 0 ; i < m_uPasses ; i++)
  {
    if (m_pPasses[i].UsesTexGen())
    {
      return true;
    }
  }

  return false;
}

void Shader::PreCache(MeshElement* pMeshElement)
{
  if (m_Flags & SH_VOXELIZE)
  {
    SM_Voxelizer::CacheMeshElement(pMeshElement);
  }
}

void Shader::Render(MeshElement** pMeshElements, unsigned uElements, bool bDoBatch)
{
  unsigned i;

  
  if (bDoBatch)
  {
    unsigned uVertexCount=0;
    unsigned uIndexCount=0;
    unsigned uStart = 0;
    unsigned uBatch = 0;

    for (unsigned k=0 ; k<uElements ; k++)
    {
      if ((uVertexCount+pMeshElements[k]->m_uVertices >= ResourceManager::GetVertexStreamSize()) ||
          (uIndexCount+pMeshElements[k]->m_uPrimitives*3 >= ResourceManager::GetIndexStreamSize()))
      {
        Render(&pMeshElements[uStart], uBatch, false);
        uStart = k;
        uBatch = 0;
        uVertexCount = 0;
        uIndexCount = 0;
      }
      
      uBatch++;
      uVertexCount+=pMeshElements[k]->m_uVertices;
      uIndexCount+=pMeshElements[k]->m_uPrimitives*3;      
    }

    if (uBatch)
    {
      Render(&pMeshElements[uStart], uBatch, false);
    }
        
    return;
  }
  
  SM_D3d::Device()->SetVertexShader(FVF_POSNORMALDIFFUSETEX1);

  if (m_Flags & SH_VOXELIZE)
  {
    // Ok, here we play a dirty trick... we will call the voxelizer which will voxelize,
    // the mesh element and callback the render but without the SH_VOXELIZE flag.
    for (i = 0 ; i < uElements ; i++)
    {
      SM_Voxelizer::DrawMeshElement(pMeshElements[i], g_pRC);
    }
    return;
  }  

  // Autosprite
  if (m_PosGen==PG_AUTOSPRITE)
  {
    Vector3D v[3];

    v[0]=Vector3D(pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[0]].x,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[0]].y,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[0]].z);

    v[1]=Vector3D(pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[1]].x,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[1]].y,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[1]].z);

    v[2]=Vector3D(pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[2]].x,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[2]].y,
                  pMeshElements[0]->m_pVertices[pMeshElements[0]->m_pIndices[2]].z);

    Vector3D R=(v[2]-v[0]).Normalize();
    Vector3D U=(v[1]-v[0]).Normalize();
    Vector3D F=(Vector3D::Cross(U, R)).Normalize();

    Matrix4X4 m   =Matrix4X4::Identity;
    m.m_11=R.x; m.m_12=U.x; m.m_13=F.x;
    m.m_21=R.y; m.m_22=U.y; m.m_23=F.y;
    m.m_31=R.z; m.m_32=U.z; m.m_33=F.z;    
    m.m_41=m.m_42=m.m_43=0.0f;

    Matrix4X4 mView;

    Matrix4X4 mAux;


    g_pRC->GetViewport()->InverseViewMatrix(mView);
    mView.m_41=mView.m_42=mView.m_43=0.0f;
    Matrix4X4::Mult(&mAux, &m,&mView);

    mAux.m_41=pMeshElements[0]->m_WorldTransform.m_41;
    mAux.m_42=pMeshElements[0]->m_WorldTransform.m_42;
    mAux.m_43=pMeshElements[0]->m_WorldTransform.m_43;
    
    pMeshElements[0]->m_WorldTransform=mAux;

    SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*) &Matrix4X4::Identity);
  }
  else
  {
    SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*) &Matrix4X4::Identity);      
  }
          
  // Render passes
  for (i=0 ; i<m_uPasses ; i++)
  {
    
    SetShaderState(i);

    unsigned uStartIndex =0;
    unsigned uStartVertex=0;

    #ifdef USESTATICBUFFERS
    if (0 || (m_pPasses[i].m_Flags  & E_DYNAMICGEOMETRY) || 
         (m_Flags & SH_DYNAMIC_GEOMETRY) ||
         uElements>1 || 
         pMeshElements[0]->m_iVB==-1 || 
         m_pVertexShader)
    #else
    if ((1 || uElements>1))
    #endif
    {
      unsigned j,k;

      // Contamos vertices
      unsigned uBatchVertices=0;
      unsigned uBatchIndices=0;

      for (k=0 ; k<uElements ; k++)
      {
        uBatchVertices+=pMeshElements[k]->m_uVertices;
        uBatchIndices +=pMeshElements[k]->m_uPrimitives*3;
      }


      // Copy vertices
      FVF_PosNormalDiffuseTex1* pVertices=0;

      int iVertexOffset;
      int iIndexOffset;
      if  ((iVertexOffset=ResourceManager::GiveVBChunk(
            ResourceManager::m_iPosNormalDiffuseTex1Stream, 
            sizeof(FVF_PosNormalDiffuseTex1)*uBatchVertices, (void**) &pVertices))==-1)
      {
        continue;
      }
      
      if (m_pVertexShader)
      {
        unsigned uVertex=0;
        for (k=0 ; k<uElements ; k++)
        {
          MeshElement* pMeshElement=pMeshElements[k];
          
          m_pVertexShader->ProcessVertices(pVertices+uVertex, 
                                                        pMeshElements[k]->m_pVertices,
                                                        pMeshElements[k]->m_uVertices,
                                                        pMeshElements[k]->m_pIndices,
                                                        pMeshElements[k]->m_uPrimitives*3, &pMeshElements[k]->m_WorldTransform);

          uVertex+=pMeshElements[k]->m_uVertices;          
        }
      }
      else if (m_pPasses[i].m_Flags & E_SCALE)
      {
        float fScale=m_pPasses[i].m_ScaleWave.Evaluate(Timer::GetTime());

        unsigned uVertex=0;            
        for (k=0 ; k<uElements ; k++)
        {
          MeshElement* pMeshElement=pMeshElements[k];
          for (j=0 ; j<pMeshElements[k]->m_uVertices ; j++)
          {
            pVertices[j+uVertex].x      = pMeshElement->m_pVertices[j].x+pMeshElement->m_pVertices[j].nx*fScale;
            pVertices[j+uVertex].y      = pMeshElement->m_pVertices[j].y+pMeshElement->m_pVertices[j].ny*fScale;
            pVertices[j+uVertex].z      = pMeshElement->m_pVertices[j].z+pMeshElement->m_pVertices[j].nz*fScale;
            pVertices[j+uVertex].nx     = pMeshElement->m_pVertices[j].nx;
            pVertices[j+uVertex].ny     = pMeshElement->m_pVertices[j].ny;
            pVertices[j+uVertex].nz     = pMeshElement->m_pVertices[j].nz;
            pVertices[j+uVertex].diffuse= pMeshElement->m_pVertices[j].diffuse;
            pVertices[j+uVertex].u      = pMeshElement->m_pVertices[j].u;
            pVertices[j+uVertex].v      = pMeshElement->m_pVertices[j].v;            
          }
          uVertex+=pMeshElements[k]->m_uVertices;
        }
      }
      else if (m_NormalGen & NG_DISTORT)
      {
        unsigned uVertex=0;            
        
        for (k=0 ; k<uElements ; k++)
        {  
          MeshElement* pMeshElement=pMeshElements[k];

          DoDistort(&pMeshElements[k]->m_WorldTransform, 
                      pVertices+uVertex, 
                      pMeshElement->m_pVertices, 
                      pMeshElements[k]->m_uVertices);


          uVertex+=pMeshElements[k]->m_uVertices;
        }
      }
      else
      {
        unsigned uVertex=0;            
        
        for (k=0 ; k<uElements ; k++)
        {  
          //memcpy(pVertices+uVertex, pMeshElements[k]->m_pVertices, sizeof(FVF_PosNormalDiffuseTex1)*pMeshElements[k]->m_uVertices);
          MeshElement* pMeshElement=pMeshElements[k];

          DoVertices(&pMeshElements[k]->m_WorldTransform, 
                      pVertices+uVertex, 
                      pMeshElement->m_pVertices, 
                      pMeshElements[k]->m_uVertices);


          uVertex+=pMeshElements[k]->m_uVertices;
        }
      }

      ResourceManager::DoneVBChunk(ResourceManager::m_iPosNormalDiffuseTex1Stream);

      // Copy indices
      unsigned short * pusIndices=0;
      if  ((iIndexOffset=ResourceManager::GiveIBChunk(
            ResourceManager::m_iIndexStream, 
            sizeof(unsigned short)*uBatchIndices , (void**) &pusIndices))==-1)
      {
        continue;
      }  
      
      unsigned uVertex=0;                      
      unsigned uIndex=0;

      for (k=0 ; k<uElements ; k++)
      {
        MeshElement* pMeshElement=pMeshElements[k];

        unsigned uElementIndices=pMeshElement->m_uPrimitives*3;
        for (j=0 ; j<uElementIndices ; j++)
        {
          pusIndices[uIndex+j]=uVertex+pMeshElement->m_pIndices[j];
        }
        
        uIndex +=uElementIndices;
        uVertex+=pMeshElement->m_uVertices;
      }

      ResourceManager::DoneIBChunk(ResourceManager::m_iIndexStream);

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

      
      SM_D3d::Device()->SetIndices(
        ResourceManager::GetIndexBufferFromID(ResourceManager::m_iIndexStream), uStartVertex);

      
      SM_D3d::Device()->SetStreamSource(
          0, 
          ResourceManager::GetVertexBufferFromID(ResourceManager::m_iPosNormalDiffuseTex1Stream), 
          sizeof(FVF_PosNormalDiffuseTex1));

      if (m_pPasses[i].m_Flags & E_MATRIXSCALE)
      {
        Matrix4X4 Scale;

        float fScale = m_pPasses[i].m_ScaleWave.Evaluate(Timer::GetTime());

        Scale.SetScale(fScale, fScale, fScale);
        SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&Scale);
      }
             

      
      SM_D3d::Device()->DrawIndexedPrimitive(
      D3DPT_TRIANGLELIST, 
      0, 
      uBatchVertices ,
      uStartIndex,
      uBatchIndices/3);        
            
      m_iPolygons+=uBatchIndices/3;                            
    }
    else
    {
      IDirect3DVertexBuffer8* pVB;
      IDirect3DIndexBuffer8 * pIB;

      pIB=ResourceManager::GetIndexBufferFromID (pMeshElements[0]->m_iIB); assert(pIB);
      pVB=ResourceManager::GetVertexBufferFromID(pMeshElements[0]->m_iVB); assert(pVB);

      SM_D3d::Device()->SetIndices(pIB, pMeshElements[0]->m_uStartVertex);
      SM_D3d::Device()->SetStreamSource(0, pVB, sizeof(FVF_PosNormalDiffuseTex1));
      SM_D3d::Device()->SetVertexShader(FVF_POSNORMALDIFFUSETEX1);    

      if (m_pPasses[i].m_Flags & E_MATRIXSCALE)
      {
        Matrix4X4 Out;
        Matrix4X4 Scale;

        float fScale = m_pPasses[i].m_ScaleWave.Evaluate(Timer::GetTime());

        Scale.SetScale(fScale, fScale, fScale);
        Matrix4X4::Mult(&Out, &Scale, &pMeshElements[0]->m_WorldTransform);        

        SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&Out);
      }
      else
      {
        SM_D3d::Device()->SetTransform(D3DTS_WORLD, (D3DMATRIX*)&pMeshElements[0]->m_WorldTransform);
      }             

      uStartIndex =pMeshElements[0]->m_uStartIndex;
      uStartVertex=pMeshElements[0]->m_uStartVertex;        

      
      SM_D3d::Device()->DrawIndexedPrimitive(
      D3DPT_TRIANGLELIST, 
      uStartVertex, 
      pMeshElements[0]->m_uVertices,
      uStartIndex,
      pMeshElements[0]->m_uPrimitives);        
      m_iPolygons+=pMeshElements[0]->m_uPrimitives;        
    }                    
  }       
}
  

MBStaticList<Shader*>   ShaderList;
MBStaticList<Texture*>  TextureList;

// Implementation

int    Init            ()
{
  ShaderList.Init();
  TextureList.Init();

  if (InitVertexShaders()!=0)
  {
    return -1;
  }

  if ((m_iNullShader=LoadCommonShader("NULL", ST_NULL, 0, 0))==-1)
  {
    return (-1);
  }

  m_iPolygons=0;

    
  return (0);
}

Shader* GetShader(int iID)
{
  Shader* pShader=0;
  ShaderList.Get(iID, pShader);

  return pShader;
}



Shader* GetShader(const char* pcShaderName)
{
  int iID=FindShader(pcShaderName);  
  if (iID==-1)
  {
    return 0;
  }

  return (GetShader(iID));
}
  
int GetShaderNameByID(int iID, const char*& pcShaderName)
{
  Shader* pShader;
  if (ShaderList.Get(iID, pShader)!=0)
  {
    return -1;
  }

  pcShaderName=pShader->m_pcShaderName;

  return 0;  
}

int    RemoveShader(int iIterator)
{
  Shader* pShader;

  ShaderList.Get(iIterator, pShader);

  pShader->ReleaseResources();
  delete pShader;

  ShaderList.Delete(iIterator);

  return 0;
}

int    Shutdown        ()
{
  if (m_iNullShader!=-1)
  {
    ReleaseShader(m_iNullShader);
    m_iNullShader = -1;
  }

  if (ShutdownVertexShaders()!=0)
  {
    return -1;
  }

  int iIterator;
  int iNext;

  for (iIterator=ShaderList.First() ; iIterator!=-1 ; iIterator=iNext)
  {
    iNext=ShaderList.Next(iIterator);
    RemoveShader(iIterator);
  }

  ShaderList.Shutdown();
  TextureList.Shutdown();
  return (0);
}



int    FindShader      (const char* pcShaderName)
{
  int i;

  char pcShaderNameUpr[256];

  strcpy(pcShaderNameUpr, pcShaderName);
  strupr(pcShaderNameUpr);
  
  for (i=ShaderList.First() ; i!=-1 ; i=ShaderList.Next(i))
  {
    Shader* pShader;

    ShaderList.Get(i, pShader);
    
    if (strcmp(pcShaderNameUpr, pShader->m_pcShaderName)==0)
    {
      return (i);
    }
  }

  return (-1);
}

int   FindTexture      (const char* pcTextureName)
{
  int i;
  
  for (i=TextureList.First() ; i!=-1 ; i=TextureList.Next(i))
  {
    Texture* pTexture;

    TextureList.Get(i, pTexture);

    if (strcmp(pcTextureName, pTexture->m_pcTextureName)==0)
    {
      return (i);
    }
  }

  return (-1);
}

int    LoadShader      (const char* pcShaderName)
{
  int iID;
  Shader* pShader;

  if ((iID=FindShader(pcShaderName))!=-1)
  {
    ShaderList.Get(iID, pShader);

    // Increment reference count
    if (!pShader->AddRef())
    {
      return (-1);
    }

    return (iID);
  }
  else 
  {
    return (LoadCommonShader(pcShaderName, ST_STANDARDTEXTURE, pcShaderName, 0));
  }

  return (-1);
}

int    LoadCommonShader(const char* pcShaderName, eCommonShaderType Type, const char* pcTextureA, const char* pcTextureB, unsigned uColor)
{
  int iID;
  Shader* pShader=0;

  char pcNormalized[MVFSMAX_PATH];
  NormalizeName(pcNormalized, pcShaderName);


  if ((iID=FindShader(pcNormalized))!=-1)
  {
    ShaderList.Get(iID, pShader);

    // Increment reference count
    pShader->m_iReferences++;
    return (iID);
  }

  switch (Type)
  {
  case ST_NULL:
    pShader=new Shader;

    if (!pShader) goto HASFAILED;

    pShader->m_PosGen               =PG_PASSTHRU;    
    pShader->m_uPasses              =1;
    pShader->m_iReferences          =0;
    
    pShader->m_pcShaderName=new char [strlen(pcNormalized)+1];
    if (!pShader->m_pcShaderName) goto HASFAILED;
    strcpy(pShader->m_pcShaderName, pcNormalized);
    
    
    
    pShader->m_pPasses=new ShaderPass[pShader->m_uPasses];
    if (!pShader->m_pPasses) goto HASFAILED;

    pShader->m_pPasses[0].m_Flags         =0;
    pShader->m_pPasses[0].m_iTextureID    =-1;
    pShader->m_pPasses[0].m_pcTextureName =0;
    pShader->m_pPasses[0].m_CullMode=CULL_NONE;
    
    pShader->m_pPasses[0].m_ColorGen      =CG_PASSTHRU;
    pShader->m_pPasses[0].m_TexGen        =TG_PASSTHRU;
    pShader->m_pPasses[0].m_SrcBlend      =BL_ONE;
    pShader->m_pPasses[0].m_DstBlend      =BL_ZERO;
    pShader->m_pPasses[0].m_ColorOp       =PO_MODULATE;
    pShader->m_pPasses[0].m_ColorArg1     =PA_TEXTURE;
    pShader->m_pPasses[0].m_ColorArg2     =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaOp       =PO_SELECTARG1;
    pShader->m_pPasses[0].m_AlphaArg1     =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaArg2     =PA_DIFFUSE;

    
    break;
  case ST_STANDARDREFLECTION:
    pShader=new Shader;

    if (!pShader) goto HASFAILED;

    pShader->m_PosGen               =PG_PASSTHRU;    
    pShader->m_uPasses              =1;
    pShader->m_iReferences          =0;

    pShader->m_pcShaderName=new char [strlen(pcNormalized)+1];
    if (!pShader->m_pcShaderName) goto HASFAILED;   
    strcpy(pShader->m_pcShaderName, pcNormalized);
    
      
    pShader->m_pPasses=new ShaderPass[pShader->m_uPasses];
    if (!pShader->m_pPasses) goto HASFAILED;
            
    pShader->m_pPasses[0].m_pcTextureName =new char[strlen(pcTextureA)+1];
    if (!pShader->m_pPasses[0].m_pcTextureName)
    {
      goto HASFAILED;
    }
    strcpy(pShader->m_pPasses[0].m_pcTextureName, pcTextureA);

    pShader->m_pPasses[0].m_Flags         =0;
    pShader->m_pPasses[0].m_ColorGen=CG_PASSTHRU;
    pShader->m_pPasses[0].m_TexGen  =TG_SPHEREREFLECTION;
    pShader->m_pPasses[0].m_SrcBlend=BL_ONE;
    pShader->m_pPasses[0].m_DstBlend=BL_ZERO;
    pShader->m_pPasses[0].m_ColorOp   =PO_MODULATE;
    pShader->m_pPasses[0].m_ColorArg1 =PA_TEXTURE;
    pShader->m_pPasses[0].m_ColorArg2 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaOp   =PO_SELECTARG1;
    pShader->m_pPasses[0].m_AlphaArg1 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaArg2 =PA_DIFFUSE;
    
    break;

  case ST_STANDARDTEXTURE:    
    pShader=new Shader;

    if (!pShader) goto HASFAILED;

    pShader->m_PosGen               =PG_PASSTHRU;    
    pShader->m_uPasses              =1;
    pShader->m_iReferences          =0;

    pShader->m_pcShaderName=new char [strlen(pcNormalized)+1];
    if (!pShader->m_pcShaderName) goto HASFAILED;   
    strcpy(pShader->m_pcShaderName, pcNormalized);
    
      
    pShader->m_pPasses=new ShaderPass[pShader->m_uPasses];
    if (!pShader->m_pPasses) goto HASFAILED;
            
    pShader->m_pPasses[0].m_pcTextureName =new char[strlen(pcTextureA)+1];
    if (!pShader->m_pPasses[0].m_pcTextureName)
    {
      goto HASFAILED;
    }
    strcpy(pShader->m_pPasses[0].m_pcTextureName, pcTextureA);

    pShader->m_pPasses[0].m_Flags         =0;
    pShader->m_pPasses[0].m_ColorGen=CG_PASSTHRU;
    pShader->m_pPasses[0].m_TexGen  =TG_PASSTHRU;
    pShader->m_pPasses[0].m_SrcBlend=BL_ONE;
    pShader->m_pPasses[0].m_DstBlend=BL_ZERO;
    pShader->m_pPasses[0].m_ColorOp   =PO_MODULATE;
    pShader->m_pPasses[0].m_ColorArg1 =PA_TEXTURE;
    pShader->m_pPasses[0].m_ColorArg2 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaOp   =PO_SELECTARG1;
    pShader->m_pPasses[0].m_AlphaArg1 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaArg2 =PA_DIFFUSE;

    break;
    
  case ST_STANDARDTEXTUREWITHREFLECTION:
    pShader=new Shader;

    if (!pShader) goto HASFAILED;

    pShader->m_PosGen               =PG_PASSTHRU;    
    pShader->m_uPasses              =2;
    pShader->m_iReferences          =0;

    pShader->m_pcShaderName=new char [strlen(pcNormalized)+1];
    if (!pShader->m_pcShaderName) goto HASFAILED;    
    strcpy(pShader->m_pcShaderName, pcNormalized);
    


    pShader->m_pPasses=new ShaderPass[pShader->m_uPasses];
    if (!pShader->m_pPasses) goto HASFAILED;    
        
    pShader->m_pPasses[0].m_pcTextureName =new char[strlen(pcTextureA)+1];
    if (!pShader->m_pPasses[0].m_pcTextureName)
    {
      goto HASFAILED;
    }
    strcpy(pShader->m_pPasses[0].m_pcTextureName, pcTextureA);
    
    pShader->m_pPasses[0].m_Flags         =0;
    pShader->m_pPasses[0].m_ColorGen=CG_PASSTHRU;
    pShader->m_pPasses[0].m_TexGen  =TG_PASSTHRU;
    pShader->m_pPasses[0].m_SrcBlend=BL_ONE;
    pShader->m_pPasses[0].m_DstBlend=BL_ZERO;
    pShader->m_pPasses[0].m_ColorOp   =PO_MODULATE;
    pShader->m_pPasses[0].m_ColorArg1 =PA_TEXTURE;
    pShader->m_pPasses[0].m_ColorArg2 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaOp   =PO_SELECTARG1;
    pShader->m_pPasses[0].m_AlphaArg1 =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaArg2 =PA_DIFFUSE;

    pShader->m_pPasses[1].m_pcTextureName =new char[strlen(pcTextureA)+1];
    if (!pShader->m_pPasses[1].m_pcTextureName)
    {
      goto HASFAILED;
    }
    strcpy(pShader->m_pPasses[1].m_pcTextureName, pcTextureB);
    
    pShader->m_pPasses[1].m_Flags         =0;
    pShader->m_pPasses[1].m_ColorGen=CG_PASSTHRU;
    pShader->m_pPasses[1].m_TexGen  =TG_SPHEREREFLECTION;
    pShader->m_pPasses[1].m_SrcBlend=BL_ZERO;
    pShader->m_pPasses[1].m_DstBlend=BL_ONE;
    pShader->m_pPasses[1].m_ColorOp   =PO_MODULATE;
    pShader->m_pPasses[1].m_ColorArg1 =PA_TEXTURE;
    pShader->m_pPasses[1].m_ColorArg2 =PA_DIFFUSE;
    pShader->m_pPasses[1].m_AlphaOp   =PO_SELECTARG1;
    pShader->m_pPasses[1].m_AlphaArg1 =PA_DIFFUSE;
    pShader->m_pPasses[1].m_AlphaArg2 =PA_DIFFUSE;
    
    break;    
  case ST_PLAINCOLOR:
    pShader=new Shader;

    if (!pShader) goto HASFAILED;

    pShader->m_PosGen               =PG_PASSTHRU;    
    pShader->m_uPasses              =1;
    pShader->m_iReferences          =0;
    
    pShader->m_pcShaderName=new char [strlen(pcNormalized)+1];
    if (!pShader->m_pcShaderName) goto HASFAILED;
    strcpy(pShader->m_pcShaderName, pcNormalized);
            
    pShader->m_pPasses=new ShaderPass[pShader->m_uPasses];
    if (!pShader->m_pPasses) goto HASFAILED;

    pShader->m_pPasses[0].m_Flags         =0;
    pShader->m_pPasses[0].m_iTextureID    =-1;
    pShader->m_pPasses[0].m_pcTextureName =0;
    pShader->m_pPasses[0].m_CullMode=CULL_NONE;
    
    pShader->m_pPasses[0].m_ColorGen      =CG_PASSTHRU;
    pShader->m_pPasses[0].m_TexGen        =TG_PASSTHRU;
    pShader->m_pPasses[0].m_SrcBlend      =BL_ONE;
    pShader->m_pPasses[0].m_DstBlend      =BL_ZERO;
    pShader->m_pPasses[0].m_ColorOp       =PO_MODULATE;
    pShader->m_pPasses[0].m_ColorArg1     =PA_TFACTOR;
    pShader->m_pPasses[0].m_ColorArg2     =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaOp       =PO_SELECTARG1;
    pShader->m_pPasses[0].m_AlphaArg1     =PA_DIFFUSE;
    pShader->m_pPasses[0].m_AlphaArg2     =PA_DIFFUSE;
    pShader->m_pPasses[0].m_uTFactor      =uColor;

    break;
  default:
    return (-1);
  }

  if (!pShader->AddRef())
  {
    goto HASFAILED;
  }      

  strupr(pShader->m_pcShaderName);
  iID=ShaderList.InsertTail(pShader);
  if (iID!=-1)
  {
    return (iID);
  }  

  // Fall through

HASFAILED:
  if (pShader) delete pShader;

  return (-1);
}

int    ReleaseShader   (int iShader)
{
  Shader* pShader;

  if (ShaderList.Get(iShader, pShader)!=0)
  {
    assert(!"Shader doesn't exist");
    return (-1);
  }

  pShader->Release();  

  return (0);
}

int    ReloadShader(int iShader)
{
	Shader* pShader;

  if (ShaderList.Get(iShader, pShader)!=0)
  {
    assert(!"Shader doesn't exist");
    return (-1);
  }

  return pShader->ReloadResources();  
}

D3DFORMAT FormatForSurface(MSurface& s)
{
  // @TODO 16 bit formats   
  D3DFORMAT RequiredFormat;
  if (s.GetPSurfaceDescriptor()->m_pfPixelFormat.m_uAMask)
  {
    if (FAILED( SM_D3d::D3D()->CheckDeviceFormat(SM_D3d::GetCurrentMode()->uDevice, 
                                   D3DDEVTYPE_HAL,
                                   SM_D3d::GetCurrentMode()->d3dFormat,
                                   0,
                                   D3DRTYPE_TEXTURE,
                                   D3DFMT_A8R8G8B8)))
    {
      RequiredFormat=D3DFMT_A4R4G4B4;    
    }
    else
    {
      RequiredFormat=D3DFMT_A8R8G8B8;    
    }
  }
  else
  {
      if (g_b32BitTextures)
      {
          RequiredFormat=D3DFMT_X8R8G8B8;
      }
      else
      {
          RequiredFormat=D3DFMT_R5G6B5;
      }      
  }

  return RequiredFormat;
}

int    LoadTexture     (const char* pcTexture)
{
  Texture* pTexture;
  int iID;
  
  char pcNormalized[MVFSMAX_PATH];
  char pcFinalFile[MVFSMAX_PATH];


  NormalizeName(pcNormalized, pcTexture);
  strcpy(pcFinalFile, "data/textures/");
  strcat(pcFinalFile, pcNormalized);
  
  if ((iID=FindTexture(pcTexture))!=-1)
  {
    TextureList.Get(iID, pTexture);

    // Increment reference count
    pTexture->m_iReferences++;
    return (iID);
  }

  pTexture=new Texture;
  if (!pTexture)
  {
    return (-1);
  }

  
  int iTextureID;    
  
  if (strlen(pcNormalized) > 5 &&
      strncmp(pcNormalized, "BUMP_", 5) == 0)
  {
    MSurface s;
  
    D3DFORMAT RequiredFormat = FormatForSurface(s);

    if (s.Load((char*)pcFinalFile, true)==-1)
    {
      goto HASFAILED;
    }

    if ((iTextureID=ResourceManager::CreateBumpmapFromSurface(&s, RequiredFormat))==-1)
    {
      goto HASFAILED;
    }
  }
  else if (strlen(pcNormalized) > 5 &&
           strncmp(pcNormalized, "CMAP_", 5) == 0)
  {
    MSurface s[6];

    char pcFinalFile[MVFSMAX_PATH];
    NormalizeName(pcNormalized, pcTexture+5);

    strcpy(pcFinalFile, "data/textures/");
    strcat(pcFinalFile, "CMAP___");
    strcat(pcFinalFile, pcNormalized);  

    int iOffset = strlen("data/textures/")+4;

    char* pcExtensions[]=
    {
      "X+",
      "X-",
      "Y+",
      "Y-",
      "Z+",
      "Z-",
    };

    unsigned i;
    for (i = 0 ; i < 6 ; i++)
    {
      strncpy(pcFinalFile+iOffset, pcExtensions[i], strlen(pcExtensions[i]));
      if (s[i].Load((char*)pcFinalFile, true)==-1)
      {
        goto HASFAILED;
      }      
    }

    D3DFORMAT RequiredFormat = FormatForSurface(s[0]);
    if ((iTextureID=ResourceManager::CreateCubemapFromSurface(&s[0], RequiredFormat))==-1)
    {
      goto HASFAILED;
    }

  }
  else
  {
    MSurface s;
  
    if (s.Load((char*)pcFinalFile, true)==-1)
    {
      goto HASFAILED;
    }

    D3DFORMAT RequiredFormat = FormatForSurface(s);
    if ((iTextureID=ResourceManager::CreateTextureFromSurface(&s, RequiredFormat))==-1)
    {
      goto HASFAILED;
    }
  }
  
  if (pTexture->Init(pcTexture, iTextureID)!=0)
  {
    goto HASFAILED;    
  }

  if ((iID=TextureList.InsertTail(pTexture))==-1)
  {
    goto HASFAILED;
  }

  return (iID);

HASFAILED:
  delete pTexture;
  return (-1);
}

int    ReleaseTexture  (int iID)
{
  Texture* pTexture;

  if (TextureList.Get(iID, pTexture)!=0)
  {
    assert(!"Texture doesn't exist");
    return (-1);
  }

  pTexture->m_iReferences--;
  if (!pTexture->m_iReferences)
  {
    pTexture->Shutdown();
    delete pTexture;
    TextureList.Delete(iID);
  }

  return (0);
}

void   Render(MeshElement** pMeshElements, unsigned uElements)
{  
  Shader* pShader;

  if (ShaderList.Get(
          pMeshElements[0]->m_iShader==-1?
          m_iNullShader:pMeshElements[0]->m_iShader, pShader)!=0)
  {
    assert(!"Shader doesn't exist");
  }
  else
  {
    pShader->Render(pMeshElements, uElements, true);
  }
}

IDirect3DBaseTexture8* GetD3DTextureFromID(int iID)
{
  Texture* pTexture;

  if (iID==-1)
  {
    return (0);
  }

  TextureList.Get(iID, pTexture);

  // As ugly as it gets :(
  IDirect3DBaseTexture8* pCubeTexture = ResourceManager::GetCubeMapFromID(pTexture->m_iD3DTextureID);
  if (pCubeTexture)
  {
    return pCubeTexture;
  }
  else
  {
    return ResourceManager::GetTextureFromID(pTexture->m_iD3DTextureID);
  }
           
}


int    ParseWave(MetaFileNode* pWaveNode, WaveForm* pWave)
{
  pWave->m_Type       =WF_CONSTANT;
  pWave->m_fBase      =0.0f;
  pWave->m_fAmplitude =0.0f;
  pWave->m_fFrequency =0.0f;
  pWave->m_fOffset    =0.0f;
  

  if (pWaveNode)
  {   
    if (pWaveNode->GetSon("TYPE"))
    {
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_CONSTANT"   )==0) pWave->m_Type=WF_CONSTANT; else 
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_SIN"        )==0) pWave->m_Type=WF_SIN;      else 
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_TRIANGLE"   )==0) pWave->m_Type=WF_TRIANGLE; else 
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_SQUARE"   )==0) pWave->m_Type=WF_SQUARE; else 
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_SAWTOOTH"   )==0) pWave->m_Type=WF_SAWTOOTH; else 
      if (strcmp(pWaveNode->GetSon("TYPE")->m_Sons[0]->m_Token.m_pszString, "WF_INVSAWTOOTH")==0) pWave->m_Type=WF_INVSAWTOOTH; 
    }

    if (pWaveNode->GetSon("BASE"))
    {
      pWave->m_fBase=pWaveNode->GetSon("BASE")->m_Sons[0]->m_Token.m_fValue;
    }

    if (pWaveNode->GetSon("AMPLITUDE"))
    {
      pWave->m_fAmplitude=pWaveNode->GetSon("AMPLITUDE")->m_Sons[0]->m_Token.m_fValue;
    }

    if (pWaveNode->GetSon("FREQUENCY"))
    {
      pWave->m_fFrequency=pWaveNode->GetSon("FREQUENCY")->m_Sons[0]->m_Token.m_fValue;
    }

    if (pWaveNode->GetSon("OFFSET"))
    {
      pWave->m_fOffset=pWaveNode->GetSon("OFFSET")->m_Sons[0]->m_Token.m_fValue;
    }

    return (1);
  }
  
  return (0);
}

int    ParsePass(MetaFileNode* pPass, ShaderPass* pShaderPass)
{
  MetaFileNode* pNode;
  MetaFileNode* pParam;
  
  // Default params.

  pShaderPass->m_Flags        =0;
  pShaderPass->m_ColorGen     =CG_PASSTHRU;
  pShaderPass->m_TexGen       =TG_PASSTHRU;
  pShaderPass->m_SrcBlend     =BL_ONE;
  pShaderPass->m_DstBlend     =BL_ZERO;
  pShaderPass->m_ColorOp      =PO_MODULATE;
  pShaderPass->m_ColorArg1    =PA_TEXTURE;
  pShaderPass->m_ColorArg2    =PA_DIFFUSE;
  pShaderPass->m_AlphaOp      =PO_SELECTARG1;
  pShaderPass->m_AlphaArg1    =PA_DIFFUSE;
  pShaderPass->m_AlphaArg2    =PA_DIFFUSE;
  pShaderPass->m_iTextureID   =-1;
  pShaderPass->m_pcTextureName=0;

  
  pNode=pPass->GetSon("LIGHTING");
  if (pNode)
  {
    pParam=pNode->GetSon("AMBIENT");
    if (pParam)
    {
      pShaderPass->m_Material.Ambient.a=1.0f;
      pShaderPass->m_Material.Ambient.r=pParam->m_Sons[0]->m_Token.m_fValue;
      pShaderPass->m_Material.Ambient.g=pParam->m_Sons[1]->m_Token.m_fValue;
      pShaderPass->m_Material.Ambient.b=pParam->m_Sons[2]->m_Token.m_fValue;
    }

    pParam=pNode->GetSon("DIFFUSE");
    if (pParam)
    {
      pShaderPass->m_Material.Diffuse.a=1.0f;
      pShaderPass->m_Material.Diffuse.r=pParam->m_Sons[0]->m_Token.m_fValue;
      pShaderPass->m_Material.Diffuse.g=pParam->m_Sons[1]->m_Token.m_fValue;
      pShaderPass->m_Material.Diffuse.b=pParam->m_Sons[2]->m_Token.m_fValue;
    }    

    pParam=pNode->GetSon("SPECULAR");
    if (pParam)
    {
      pShaderPass->m_Material.Specular.a=1.0f;
      pShaderPass->m_Material.Specular.r=pParam->m_Sons[0]->m_Token.m_fValue;
      pShaderPass->m_Material.Specular.g=pParam->m_Sons[1]->m_Token.m_fValue;
      pShaderPass->m_Material.Specular.b=pParam->m_Sons[2]->m_Token.m_fValue;      
    }

    
    pParam=pNode->GetSon("POWER");
    if (pParam)
    {
      pShaderPass->m_Material.Power=pParam->m_Sons[0]->m_Token.m_fValue;
    }
  }
  

  pNode=pPass->GetSon("TEXTURE");
  if (pNode)
  {
    pShaderPass->m_pcTextureName=new char[strlen(pNode->m_Sons[0]->m_Token.m_pszString)+1];
    if (!pShaderPass->m_pcTextureName)
    {
      return (-1);
    }
    
    strcpy(pShaderPass->m_pcTextureName, pNode->m_Sons[0]->m_Token.m_pszString);
  }

  pNode=pPass->GetSon("MIP");
  if (pNode)
  {
    if (strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "MIP_NONE")==0)
    {
      pShaderPass->m_MipFilter=MIP_NONE;
    }
    else if (strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "MIP_POINT")==0)
    {
      pShaderPass->m_MipFilter=MIP_POINT;
    }
    else if (strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "MIP_LINEAR")==0)
    {
      pShaderPass->m_MipFilter=MIP_LINEAR;
    }
  }

  pNode=pPass->GetSon("TEXGEN");
  if (pNode)
  {
    pParam=pNode->GetSon("TYPE");

    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "TG_PASSTHRU")==0)
      {
        pShaderPass->m_TexGen=TG_PASSTHRU;
      }
      else if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "TG_SPHEREREFLECTION")==0)
      {
        pShaderPass->m_TexGen=TG_SPHEREREFLECTION;
      }
      else if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "TG_CAMERAREFLECTION")==0)
      {
        pShaderPass->m_TexGen=TG_CAMERAREFLECTION;
      }
      else if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "TG_CUBEREFLECTION")==0)
      {
        pShaderPass->m_TexGen=TG_CUBEREFLECTION;
      }

    }
  }

  pNode=pPass->GetSon("BLEND");
  if (pNode)
  {
    pParam=pNode->GetSon("SRC");

    if (pParam)
    {      
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_ZERO")==0) pShaderPass->m_SrcBlend=BL_ZERO; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_ONE" )==0) pShaderPass->m_SrcBlend=BL_ONE ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVSRCCOLOR")==0) pShaderPass->m_SrcBlend=BL_INVSRCCOLOR; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_SRCALPHA" )==0) pShaderPass->m_SrcBlend=BL_SRCALPHA ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVSRCALPHA")==0) pShaderPass->m_SrcBlend=BL_INVSRCALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_DESTALPHA" )==0) pShaderPass->m_SrcBlend=BL_DESTALPHA ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVDESTALPHA")==0) pShaderPass->m_SrcBlend=BL_INVDESTALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_DESTCOLOR" )==0) pShaderPass->m_SrcBlend=BL_DESTCOLOR ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVDESTCOLOR")==0) pShaderPass->m_SrcBlend=BL_INVDESTCOLOR; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_SRCALPHASAT" )==0) pShaderPass->m_SrcBlend=BL_SRCALPHASAT ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_BOTHSRCALPHA")==0) pShaderPass->m_SrcBlend=BL_BOTHSRCALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_BOTHINVSRCALPHA" )==0) pShaderPass->m_SrcBlend=BL_BOTHINVSRCALPHA ;                
    }

    pParam=pNode->GetSon("DST");

    if (pParam)
    {      
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_ZERO")==0) pShaderPass->m_DstBlend=BL_ZERO; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_ONE" )==0) pShaderPass->m_DstBlend=BL_ONE ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVSRCCOLOR")==0) pShaderPass->m_DstBlend=BL_INVSRCCOLOR; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_SRCALPHA" )==0) pShaderPass->m_DstBlend=BL_SRCALPHA ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVSRCALPHA")==0) pShaderPass->m_DstBlend=BL_INVSRCALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_DESTALPHA" )==0) pShaderPass->m_DstBlend=BL_DESTALPHA ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVDESTALPHA")==0) pShaderPass->m_DstBlend=BL_INVDESTALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_DESTCOLOR" )==0) pShaderPass->m_DstBlend=BL_DESTCOLOR ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_INVDESTCOLOR")==0) pShaderPass->m_DstBlend=BL_INVDESTCOLOR; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_SRCALPHASAT" )==0) pShaderPass->m_DstBlend=BL_SRCALPHASAT ; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_BOTHSRCALPHA")==0) pShaderPass->m_DstBlend=BL_BOTHSRCALPHA; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "BL_BOTHINVSRCALPHA" )==0) pShaderPass->m_DstBlend=BL_BOTHINVSRCALPHA ;                
    }
  }

  
  pNode=pPass->GetSon("COLOR");
  if (pNode)
  {
    pParam=pNode->GetSon("OP");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_SELECTARG1")==0) pShaderPass->m_ColorOp=PO_SELECTARG1; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_SELECTARG2")==0) pShaderPass->m_ColorOp=PO_SELECTARG2; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_MODULATE")  ==0) pShaderPass->m_ColorOp=PO_MODULATE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_DOT3")  ==0) pShaderPass->m_ColorOp=PO_DOT3; else         
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_ADD")       ==0) pShaderPass->m_ColorOp=PO_ADD; 
    }

    pParam=pNode->GetSon("ARG1");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_DIFFUSE")==0) pShaderPass->m_ColorArg1=PA_DIFFUSE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TEXTURE")==0) pShaderPass->m_ColorArg1=PA_TEXTURE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TFACTOR")==0) pShaderPass->m_ColorArg1=PA_TFACTOR;       
    }

    pParam=pNode->GetSon("ARG2");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_DIFFUSE")==0) pShaderPass->m_ColorArg2=PA_DIFFUSE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TEXTURE")==0) pShaderPass->m_ColorArg2=PA_TEXTURE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TFACTOR")==0) pShaderPass->m_ColorArg2=PA_TFACTOR;       
    }
  }

  pNode=pPass->GetSon("ALPHA");
  if (pNode)
  {
    pParam=pNode->GetSon("OP");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_SELECTARG1")==0) pShaderPass->m_AlphaOp=PO_SELECTARG1; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_SELECTARG2")==0) pShaderPass->m_AlphaOp=PO_SELECTARG2; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_MODULATE")  ==0) pShaderPass->m_AlphaOp=PO_MODULATE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PO_ADD")       ==0) pShaderPass->m_AlphaOp=PO_ADD; 
    }

    pParam=pNode->GetSon("ARG1");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_DIFFUSE")==0) pShaderPass->m_AlphaArg1=PA_DIFFUSE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TEXTURE")==0) pShaderPass->m_AlphaArg1=PA_TEXTURE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TFACTOR")==0) pShaderPass->m_AlphaArg1=PA_TFACTOR;       
    }

    pParam=pNode->GetSon("ARG2");
    if (pParam)
    {
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_DIFFUSE")==0) pShaderPass->m_AlphaArg2=PA_DIFFUSE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TEXTURE")==0) pShaderPass->m_AlphaArg2=PA_TEXTURE; else 
      if (pParam && strcmp(pParam->m_Sons[0]->m_Token.m_pszString, "PA_TFACTOR")==0) pShaderPass->m_AlphaArg2=PA_TFACTOR;       
    }
  }  

  pNode=pPass->GetSon("TFACTORALPHA");
  if (pNode)
  {
    if (ParseWave(pNode->GetSon("WAVE"), &pShaderPass->m_AlphaWave))
    {
      pShaderPass->m_Flags|=E_ALPHAWAVE;
    }
  }  

  pNode=pPass->GetSon("SPECULARHIGHLIGHT");
  if (pNode)
  {
    pShaderPass->m_Flags|=E_SPECULARADD;
  }
  
  pNode=pPass->GetSon("TFACTORCOLOR");
  if (pNode)
  {
    if (ParseWave(pNode->GetSon("WAVE"), &pShaderPass->m_ColorWave))
    {
      pShaderPass->m_Flags|=E_COLORWAVE;
    }
    else
    if (pParam=pNode->GetSon("RGB"))
    {
      float r=pParam->m_Sons[0]->m_Token.m_fValue;
      float g=pParam->m_Sons[1]->m_Token.m_fValue;
      float b=pParam->m_Sons[2]->m_Token.m_fValue;
      pShaderPass->m_uTFactor=
        (int(r*255.0f)<<16) | (int(g*255.0f)<<8) | (int(b*255.0f));
    }
  }  

  pNode=pPass->GetSon("CULL");
  if (pNode)
  {
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "CULL_NONE")==0) pShaderPass->m_CullMode=CULL_NONE; else 
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "CULL_CW")==0) pShaderPass->m_CullMode=CULL_CW; else 
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "CULL_CCW")==0) pShaderPass->m_CullMode=CULL_CCW;       
  }  


  pNode=pPass->GetSon("FILLMODE");
  if (pNode)
  {
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "SOLID")==0) pShaderPass->m_FillMode=RM_SOLID; else 
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "WIREFRAME")==0) pShaderPass->m_FillMode=RM_WIREFRAME;     
  }  

  pNode=pPass->GetSon("SHADEMODE");
  if (pNode)
  {
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "FLAT")==0) pShaderPass->m_ShadeMode=SM_FLAT; 
  }  

  pNode=pPass->GetSon("SCALE");
  if (pNode)
  {
    if (ParseWave(pNode->GetSon("WAVE"), &pShaderPass->m_ScaleWave))
    {
      pShaderPass->m_Flags|= E_DYNAMICGEOMETRY | E_SCALE; 
    }
  }  

  pNode=pPass->GetSon("MATRIXSCALE");
  if (pNode)
  {
    if (ParseWave(pNode->GetSon("WAVE"), &pShaderPass->m_ScaleWave))
    {
      pShaderPass->m_Flags|= E_MATRIXSCALE;
    }
  }  


  pNode=pPass->GetSon("NOZWRITE");
  if (pNode)
  {    
    pShaderPass->m_Flags|= E_NOZWRITE;
  }  

  pNode=pPass->GetSon("ZCMP");
  if (pNode)
  {
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "ALWAYS")==0) pShaderPass->m_ZComp=ZC_ALWAYS; else 
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "LESS")==0) pShaderPass->m_ZComp=ZC_LESS;  else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "LESSEQUAL")==0) pShaderPass->m_ZComp=ZC_LESSEQUAL;  
  }

   
  return (0);
}

int    SetTFactorForShader(int iShader, unsigned uColor)
{
  Shader* pShader;
  if (ShaderList.Get(iShader, pShader)!=0)
  {
    return -1;
  }

  unsigned i;
  for (i=0 ; i<pShader->m_uPasses ; i++)
  {
    if (pShader->m_pPasses[i].m_ColorArg1==PA_TFACTOR)
    {
      pShader->m_pPasses[i].m_uTFactor=uColor;
    }

    if (pShader->m_pPasses[i].m_ColorArg2==PA_TFACTOR)
    {
      pShader->m_pPasses[i].m_uTFactor=uColor;
    }

    pShader->m_pPasses[i].m_AlphaOp  =PO_SELECTARG1;
    pShader->m_pPasses[i].m_AlphaArg1=PA_TFACTOR;
    pShader->m_pPasses[i].m_SrcBlend =BL_SRCALPHA;
    pShader->m_pPasses[i].m_DstBlend =BL_INVSRCALPHA;      
  }
    
  return 0;
}

int    ParseShader(MetaFileNode* pShaderNode)
{

  MetaFileNode* pName;
  MetaFileNode* pNode;  
  Shader*       pShader =0;
  int           iReturn =-1;
  int           iID     =-1;
  unsigned      uPasses =0;
  unsigned      uActual =0;
  

  pName=pShaderNode->GetSon("NAME");
  if (!pName)
  {
    SM_Main::OutputError("Error: Shader has no name");    
  }

  // Check if it's already loaded
  if (FindShader(pName->m_Sons[0]->m_Token.m_pszString)!=-1)
  {
    SM_Main::OutputError("Error: Shader %s has already been parsed", pName->m_Sons[0]->m_Token.m_pszString);    
    goto FAIL;
  }


  pShader=new Shader;
  if (!pShader)
  {
    goto FAIL;
  }

  // Count number of passes
  unsigned i;
  for (i=0 ; i<pShaderNode->m_Sons.size() ; i++)
  {
    if (pShaderNode->m_Sons[i]->IsTag("PASS"))
    {
      uPasses++;
    }
  }

  pShader->m_uPasses     =uPasses;
  pShader->m_pPasses     =new ShaderPass[pShader->m_uPasses];
  pShader->m_pcShaderName=new char[strlen(pName->m_Sons[0]->m_Token.m_pszString)+1];
  if (!pShader->m_pcShaderName)
  {
    goto FAIL;  
  }
  strcpy(pShader->m_pcShaderName, pName->m_Sons[0]->m_Token.m_pszString);
  strupr(pShader->m_pcShaderName);
  pShader->m_iReferences          =0;
  pShader->m_PosGen               =PG_PASSTHRU;
  
    


  // Parse passes
  for (i=0 ; i<pShaderNode->m_Sons.size() ; i++)
  {
    if (pShaderNode->m_Sons[i]->IsTag("PASS"))
    {
      if (ParsePass(pShaderNode->m_Sons[i], &(pShader->m_pPasses[uActual]))==-1)
      {
        goto FAIL;
      }
      uActual++;
    }   
  }

  
  if (pNode=pShaderNode->GetSon("VOXELIZE"))
  {
    pShader->m_Flags |= SH_VOXELIZE;

    if (pNode->GetSon("NOCULL"))
    {
      pShader->m_Flags |= SH_VOXELIZE_NOCULL;
    }    

    if (pNode->GetSon("STATIC"))
    {
      pShader->m_Flags |= SH_VOXELIZE_STATIC;
    }        
  }

  
  pNode=pShaderNode->GetSon("AUTOSPRITE");
  if (pNode)
  {
    pShader->m_PosGen=PG_AUTOSPRITE;
    pShader->m_Flags|=SH_DYNAMIC_GEOMETRY;

  }

  pNode=pShaderNode->GetSon("DISTORTNORMAL");
  if (pNode)
  {
    pShader->m_NormalGen=NG_DISTORT;
    pShader->m_Flags|=SH_DYNAMIC_GEOMETRY;
  }


  
  pNode=pShaderNode->GetSon("PRIORITY");
  if (pNode)
  {
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_FIRST")==0) pShader->m_uPriority=PR_FIRST; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_OPAQUE")==0) pShader->m_uPriority=PR_OPAQUE; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_ADDITIVE")==0) pShader->m_uPriority=PR_ADDITIVE; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_ADDITIVE2")==0) pShader->m_uPriority=PR_ADDITIVE2; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_ADDITIVE3")==0) pShader->m_uPriority=PR_ADDITIVE3; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_TRANSPARENT")==0) pShader->m_uPriority=PR_TRANSPARENT; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_TRANSPARENT2")==0) pShader->m_uPriority=PR_TRANSPARENT2; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_TRANSPARENT3")==0) pShader->m_uPriority=PR_TRANSPARENT3; else
    if (pNode && strcmp(pNode->m_Sons[0]->m_Token.m_pszString, "PR_LAST")==0) pShader->m_uPriority=PR_LAST; 
  }  

  pNode=pShaderNode->GetSon("RECEIVESHADOWS");
  if (pNode)
  {
    pShader->m_bReceiveShadows = true;
  }
  else
  {
    pShader->m_bReceiveShadows = false;
  }

  pNode=pShaderNode->GetSon("PROJECTSHADOWS");
  if (pNode)
  {
    pShader->m_bProjectShadows = true;
  }
  else
  {
    pShader->m_bProjectShadows = false;
  }

  pNode=pShaderNode->GetSon("PROJECTBEAMS");
  if (pNode)
  {
    pShader->m_bProjectBeams = true;
  }
  else
  {
    pShader->m_bProjectBeams = false;
  }

  

  iID=ShaderList.InsertTail(pShader);
  if (iID==-1)
  {
    goto FAIL;
  }  

  SM_Main::OutputConsole("Parsed shader %s\n", pName->m_Sons[0]->m_Token.m_pszString);    

  return (iID);

FAIL:
  SM_Main::OutputConsole("Failed to parse shader %s\n", pName->m_Sons[0]->m_Token.m_pszString);    

  if (pShader) { delete pShader; pShader=0; }

  return (iReturn);
}


int    ParseShadersDir (const char* pcShaderDir)
{
  if (MVFS::findfilestart(pcShaderDir, "*.txt"))
  {
    char pcShaderFile[256];
    while (MVFS::findfileget(pcShaderFile))
    {
      if (ParseShadersFile(pcShaderFile)==-1)
      {
        return -1;
      }
    }
  }

  MVFS::findfileend();
  
  return 0;
}

int    ParseShadersFile(const char* pcShaderFileName)
{
  MetaFile File;

  if (File.Load(pcShaderFileName)!=0)
  {
    SM_Main::OutputError("Error: Error parsing %s", pcShaderFileName);
    return (-1);
  }

  MetaFileNode* pSceneRoot=File.Root();

  unsigned i;
  for (i=0 ; i<pSceneRoot->m_Sons.size() ; i++)
  {
    if (pSceneRoot->m_Sons[i]->IsTag("SHADER"))
    {
      if (ParseShader(pSceneRoot->m_Sons[i])==-1)
      {
        SM_Main::OutputError("Error parsing %s\n",pcShaderFileName);
        return (-1);
      }
    }
  }


  SM_Main::OutputConsole("Succesfully parsed %s\n",pcShaderFileName);


  return (0);
}

int    GetPolygons     ()
{
  return (m_iPolygons);
}

int    AddPolygons(int iPolygons)
{
  m_iPolygons += iPolygons;
  return 0;
}

}

namespace RenderPipeline
{
  
  MBinArray<MeshElement*> m_Buckets[ShaderManager::PR_LEVELS];

  int     Init      ();
  int     Shutdown  ();

  int _cdecl ElementCmp(const void* _a, const void* _b)
  {
    MeshElement* a=*((MeshElement**) _a);
    MeshElement* b=*((MeshElement**) _b);

    if (a->m_iShader==b->m_iShader) return (a->m_fDepth>b->m_fDepth)?-1:1;

    return a->m_iShader-b->m_iShader;
  }


  int _cdecl ElementCmpInverse(const void* _a, const void* _b)
  {
    MeshElement* a=*((MeshElement**) _a);
    MeshElement* b=*((MeshElement**) _b);

    if (a->m_iShader==b->m_iShader) return (a->m_fDepth>b->m_fDepth)?1:-1;

    return a->m_iShader-b->m_iShader;
  }


  void     Flush     ()
  {

    int i,j,k;


    for (i=0 ; i<ShaderManager::PR_LEVELS ; i++)
    {
      if (i==ShaderManager::PR_OPAQUE)
      {
        qsort(m_Buckets[i].Get(), m_Buckets[i].Used(), sizeof(MeshElement*), ElementCmpInverse);        
      }
      else
      {
        qsort(m_Buckets[i].Get(), m_Buckets[i].Used(), sizeof(MeshElement*), ElementCmp);
        
      }
      
      int iSize=m_Buckets[i].Used();
      
      for (j=0 ; j<iSize ; j+=k /*j++*/)
      {
       
        unsigned uVertices;
        unsigned uIndices; 

      
        int iShader=m_Buckets[i].Get(j)->m_iShader;

        ShaderManager::Shader* pShader;        
        ShaderManager::ShaderList.Get(iShader, pShader);

        uVertices=m_Buckets[i].Get(j)->m_uVertices;
        uIndices =m_Buckets[i].Get(j)->m_uPrimitives*3;

        for (k=j+1 ;
                k<iSize && 
                m_Buckets[i].Get(k)->m_iShader==iShader &&
                (pShader->m_Flags & ShaderManager::SH_DYNAMIC_GEOMETRY)
                ; k++)
        {
          uVertices+=m_Buckets[i].Get(k)->m_uVertices;
          uIndices +=m_Buckets[i].Get(k)->m_uPrimitives*3;
        }
        k-=j;


        ShaderManager::Render(m_Buckets[i].GetP(j), k);

        
      }

      m_Buckets[i].Reset();
    }    
  }

  void    Render    (MeshElement* pMeshElement)
  {
    ShaderManager::Shader* pShader;
    if (ShaderManager::ShaderList.Get(pMeshElement->m_iShader, pShader)!=0)
    {
      return;
    }

    #if 0
    pShader->Render(pMeshElement);
    #else

    //assert(pMeshElement->m_uVertices <= ResourceManager::GetVertexStreamSize());
        
    m_Buckets[pShader->m_uPriority].Add(pMeshElement);   
    #endif
  }
}

void ShaderManager::ToggleWireframe  ()
{
  g_bWireframe = !g_bWireframe;
}

