/*======================================================================*/
/* 3DLib								*/
/*									*/
/* Material-related functions						*/
/*									*/
/* AUTHOR:	Gabor Nagy						*/
/* DATE:	1996-Jan-09 23:11:43					*/
/*									*/
/* 3DLib(TM) Copyright (C) 1995 by Gabor Nagy. All rights reserved.	*/
/*======================================================================*/
#include <assert.h>
#include <stdio.h>
#include <float.h>
#include <math.h>

#include <ELists.h>
#include <EMalloc.h>
#include <EStrings.h>

#include <Color/Color.h>

#include <E3D/E3D.h>

#ifndef _E3DScene_h
#include <E3D/Scene.h>
#endif


E3dShaderClass**	E3d_ShaderClasses=NULL;
int			E3d_NumOfShaderClasses=0;


//----------------------------------------------------------------
// Function prototypes
//----------------------------------------------------------------
#ifdef USEOpenGL
void E3d_MaterialConvertToGL(E3dMaterial* LMaterial);
#endif // USEOpenGL

E3dMaterial	E3d_DefaultMaterial;
E3d2DTexture	E3d_Default2DTexture;
E3d2DTexture*	E3d_Default2DTextureList[1] = { &E3d_Default2DTexture };
E3d3DTexture	E3d_Default3DTexture;
E3d3DTexture*	E3d_Default3DTextureList[1] = { &E3d_Default3DTexture };

E3dMaterial	E3d_PlasterMaterial;		// For drawing textured objects


//================================================================================
// Print out the attributes of a Material
//
// Argument
//  E3dMaterial*  LMaterial        The E3dMaterial to dump out
//
// Description
//  Prints the attributes of the given Material to the standard output.	
//================================================================================
void E3d_PrintMaterial(E3dMaterial* LMaterial)
{
 unsigned int	LC, LN;

 printf("  Name: %s [%08x] RefCnt: %d\n", LMaterial->Name, (unsigned int)LMaterial, LMaterial->RefCnt);

 printf("  Type: ");
 switch(LMaterial->Type)
 {
  case E3dMAT_NONE:		printf("NONE\n");break;
  case E3dMAT_BLINN:		printf("BLINN\n");break;
  case E3dMAT_PHONG:		printf("PHONG\n");break;
  case E3dMAT_LAMBERT:		printf("LAMBERT\n");break;
  case E3dMAT_CONSTANT:		printf("CONSTANT\n");break;
  case E3dMAT_SHADOW:		printf("SHADOW\n");break;
  case E3dMAT_LIGHTSOURCE:	printf("LIGHTSOURCE\n");break;
 }

 printf("  Ambient:  %f,%f,%f\n", LMaterial->Ambient.R, LMaterial->Ambient.G, LMaterial->Ambient.B);
 printf("  Diffuse:  %f,%f,%f\n", LMaterial->Diffuse.R, LMaterial->Diffuse.G, LMaterial->Diffuse.B);
 printf("  Specular: %f,%f,%f\n", LMaterial->Specular.R, LMaterial->Specular.G, LMaterial->Specular.B);
 printf("  Emission: %f,%f,%f\n", LMaterial->Emission.R, LMaterial->Emission.G, LMaterial->Emission.B);
 printf("  Transparent: %f,%f,%f\n", LMaterial->Transparent.R, LMaterial->Transparent.G, LMaterial->Transparent.B);

 printf("  Specularity: %f\n", LMaterial->Specularity);

 printf("  Reflectivity: %f\n", LMaterial->Reflectivity);
 printf("  Refractive index: %f\n", LMaterial->RefractiveIndex);
 printf("  Transparency: %f\n", LMaterial->Transparency);
 printf("  2DTextures: %d\n", LMaterial->NumOf2DTextures);

 if((LN=LMaterial->NumOf2DTextures)>0)
 {
  for(LC=0;LC<LN;LC++)
  {
   E3d_Print2DTexture(LMaterial->Textures2D[LC]);
  }
 }
}


//================================================================================
// Append a 2DTexture to a Material
//
// Arguments
//  E3dMaterial*  LMaterial        Pointer to the Material
//  E3d2DTexture* L2DTexture       Pointer to the 2DTexture
//
// Description
//  Appends the given 2DTexture to the given Material's dynamically allocated
//  Textures2D array.
//================================================================================
void E3d_MaterialAppend2DTexture(E3dMaterial* LMaterial, E3d2DTexture* L2DTexture)
{
 E3d2DTexture**	L2DTextures;

 if(LMaterial->Textures2D==NULL)
 {
  if((L2DTextures=(E3d2DTexture**)EMalloc(sizeof(E3d2DTexture*)))!=NULL) LMaterial->Textures2D=L2DTextures;
 }
 else
 {
  if((L2DTextures=(E3d2DTexture**)ERealloc(LMaterial->Textures2D, sizeof(E3d2DTexture*)*(LMaterial->NumOf2DTextures+1)))!=NULL)
  {
   LMaterial->Textures2D=L2DTextures;
  }
 }
 if(L2DTextures)
 {
  L2DTextures[LMaterial->NumOf2DTextures]=L2DTexture;
  L2DTexture->RefCnt+=1;
  LMaterial->NumOf2DTextures+=1;
  if(LMaterial->Current2DTextureIndex<0) LMaterial->Current2DTextureIndex=0;
 }
}


//================================================================================
// Add a 2DTexture to a Material
//
// Arguments
//  E3dMaterial*  LMaterial        Pointer to the Materialk
//
// Description
//  Creates a new 2DTexture and adds it to the given Material.
//
// Return value
//  Pointer to the new 2DTexture, or NULL in case of an error
//================================================================================
E3d2DTexture* E3d_MaterialAdd2DTexture(E3dMaterial* LMaterial)
{
 E3d2DTexture*	L2DTexture=E3d_2DTextureAllocate();


 if(L2DTexture)
 {
  E3d2DTexture**	L2DTextures;

  if(LMaterial->Textures2D==NULL)
  {
   if((L2DTextures=(E3d2DTexture**)EMalloc(sizeof(E3d2DTexture*)))!=NULL) LMaterial->Textures2D=L2DTextures;
  }
  else
  {
   if((L2DTextures=(E3d2DTexture**)ERealloc(LMaterial->Textures2D, sizeof(E3d2DTexture*)*(LMaterial->NumOf2DTextures+1)))!=NULL)
   {
    LMaterial->Textures2D=L2DTextures;
   }
  }
  if(L2DTextures)
  {
   L2DTextures[LMaterial->NumOf2DTextures]=L2DTexture;
   L2DTexture->RefCnt+=1;
   LMaterial->NumOf2DTextures+=1;
   if(LMaterial->Current2DTextureIndex<0) LMaterial->Current2DTextureIndex=0;
  }
 }

 return(L2DTexture);
}


//================================================================================
// Remove a 2DTexture from a Material
//
// Arguments
//  E3dMaterial*  LMaterial        Pointer to the Material
//  E3d2DTexture* L2DTexture       Pointer to the 2DTexture
//
// Description
//  Removes the given 2DTexture from the given Material's dynamically allocated
//  Textures2D array.
//================================================================================
void E3d_MaterialRemove2DTexture(E3dMaterial* LMaterial, E3d2DTexture* L2DTexture)
{
 E3d2DTexture**	L2DTextures;

 if((L2DTextures=LMaterial->Textures2D)==NULL) return;

 if(ELst_RemovePointer((void***)(&(LMaterial->Textures2D)), &(LMaterial->NumOf2DTextures), L2DTexture))
 {
  E3d_2DTextureFree(L2DTexture);

//printf("MaterialRemove2DTexture() %d %d\n", LMaterial->Current2DTextureIndex, LMaterial->NumOf2DTextures);fflush(stdout);

  if(LMaterial->Current2DTextureIndex>=LMaterial->NumOf2DTextures) LMaterial->Current2DTextureIndex=LMaterial->NumOf2DTextures-1;
 }
}


//================================================================================
// Initialize a Material to default
//
// Argument
//  E3dMaterial*  LMaterial        The E3dMaterial to be initialized
//
// Description
//  Prints the attributes of the given Material to the standard output.
//
// See also
//  E3d_MaterialAllocate, E3d_MaterialFree
//================================================================================
void E3d_MaterialDefault(E3dMaterial* LMaterial)
{
 LMaterial->Name=NULL;
 LMaterial->Type=E3dMAT_PHONG;
 LMaterial->Shader=NULL;
 LMaterial->Ambient.R=0.0;LMaterial->Ambient.G=0.0;LMaterial->Ambient.B=0.0;
 LMaterial->Diffuse.R=0.15;LMaterial->Diffuse.G=0.7;LMaterial->Diffuse.B=0.8;
 LMaterial->Specular.R=0.2;LMaterial->Specular.G=0.2;LMaterial->Specular.B=0.2;
 LMaterial->Emission.R=0.0;LMaterial->Emission.G=0.0;LMaterial->Emission.B=0.0;
 LMaterial->Reflective.R=1.0;LMaterial->Reflective.G=1.0;LMaterial->Reflective.B=1.0;
 LMaterial->Transparent.R=1.0;LMaterial->Transparent.G=1.0;LMaterial->Transparent.B=1.0;
 LMaterial->Absorption.R=0.005;LMaterial->Absorption.G=0.005;LMaterial->Absorption.B=0.005;

 LMaterial->Specularity=10.0;
 LMaterial->Reflectivity=0.0;
 LMaterial->Transparency=0.0;
 LMaterial->RefractiveIndex=1.0;

 LMaterial->Glow=0.0;

 LMaterial->NumOf2DTextures=0;LMaterial->Current2DTextureIndex=0;LMaterial->Textures2D=NULL;
 LMaterial->NumOf3DTextures=0;LMaterial->Current3DTextureIndex=0;LMaterial->Textures3D=NULL;


 E3d_3DPositionInit(&(LMaterial->SchemTranslation), 0.0, 0.0, 0.0);
 E3d_3DPositionInit(&(LMaterial->SchemPosition), 0.0, 0.0, 0.0);
 LMaterial->Sample=NULL;

#ifdef USEOpenGL
 {
  E3dGLMaterial*	LGLMaterial=&(LMaterial->GLMaterial);

  LGLMaterial->BlendingFunction=E3dBLENDF_ONE_ZERO;
 }

 E3d_MaterialConvertToGL(LMaterial);
#endif // USEOpenGL
}


//================================================================================
// Allocate memory for a Material
//
// Description
//  Allocates memory for an E3dMaterial structure and returns the pointer to the
//  allocated memory block, or NULL in case of an error.
//
// See also
//  E3d_MaterialFree, E3d_MaterialDefault
//================================================================================
E3dMaterial* E3d_MaterialAllocate()
{ 
 E3dMaterial*	LMaterial;

#ifdef E_MEMDEBUG
// assert(0);
 E_MemName="Material by E3d_MaterialAllocate()";
#endif // E_MEMDEBUG

 if((LMaterial=(E3dMaterial*)EMalloc(sizeof(E3dMaterial)))!=NULL)
 {
  LMaterial->RefCnt=0;
  E3d_MaterialDefault(LMaterial);

  return(LMaterial);
 }
 return(NULL);
}


//================================================================================
// Copy contents to a Material into another one	
//
// Description
//  Copy fields of LSrcMaterial to LDstMaterial without interfering with
//  reference counting etc.
//
// See also
//  E3d_MaterialFree, E3d_MaterialDefault
//================================================================================
void E3d_MaterialCopyContents(E3dMaterial* LSrcMaterial, E3dMaterial* LDstMaterial)
{ 
 if(LDstMaterial->Name) EFree(LDstMaterial->Name);
 LDstMaterial->Name=EStrDup(LSrcMaterial->Name);

 LDstMaterial->Type=LSrcMaterial->Type;
 LDstMaterial->Shader=LSrcMaterial->Shader;
 if(LDstMaterial->Shader) LDstMaterial->Shader->RefCnt+=1;
 LDstMaterial->Ambient=LSrcMaterial->Ambient;
 LDstMaterial->Diffuse=LSrcMaterial->Diffuse;
 LDstMaterial->Specular=LSrcMaterial->Specular;
 LDstMaterial->Emission=LSrcMaterial->Emission;
 LDstMaterial->Transparent=LSrcMaterial->Transparent;
 LDstMaterial->Specularity=LSrcMaterial->Specularity;
 LDstMaterial->Reflectivity=LSrcMaterial->Reflectivity;
 LDstMaterial->Transparency=LSrcMaterial->Transparency;
 LDstMaterial->RefractiveIndex=LSrcMaterial->RefractiveIndex;
 LDstMaterial->Glow=LSrcMaterial->Glow;

#ifdef USEOpenGL
 E3d_MaterialConvertToGL(LDstMaterial);
#endif // USEOpenGL

/*
 if((LN=LSrcMaterial->NumOf2DTextures)>0)
 {
  if((LDstMaterial->Textures2D=(E3d2DTexture**)EMalloc(sizeof(E3d2DTexture*)*LN))!=NULL)
  {
   LDstMaterial->NumOf2DTextures=LN;
   for(LC=0;LC<LN;LC++)
   {
    LDstMaterial->Textures2D[LC]=E3d_2DTextureClone(LSrcMaterial->Textures2D[LC]);
    LDstMaterial->Textures2D[LC]->RefCnt+=1;
   }
  }
 }
*/

/*
 if((LN=LSrcMaterial->NumOf3DTextures)>0)
 {
  if((LDstMaterial->Textures3D=(E3d3DTexture**)EMalloc(sizeof(E3d3DTexture*)*LN))!=NULL)
  {
   LDstMaterial->NumOf3DTextures=LN;
   for(LC=0;LC<LN;LC++)
   {
    LDstMaterial->Textures3D[LC]=E3d_3DTextureClone(LSrcMaterial->Textures3D[LC]);
    LDstMaterial->Textures3D[LC]->RefCnt+=1;
   }
  }
 }
*/

 E3d_MaterialUpdateForDisplay(LDstMaterial);
}


//========================================
// Allocate Materials array
//========================================
E3dMaterial** E3d_MaterialsAllocate(unsigned int LMaterialNum)
{ 
 E3dMaterial**	LMaterials;
 E3dMaterial*	LMaterial;
 unsigned int	LMCnt;


 if(LMaterialNum>0)
 {
#ifdef E_MEMDEBUG
  E_MemName="MaterialList";
#endif // E_MEMDEBUG
  if((LMaterials=(E3dMaterial**)EMalloc(sizeof(E3dMaterial*)*LMaterialNum))!=NULL)
  {
   for(LMCnt=0;LMCnt<LMaterialNum;LMCnt++)
   {
#ifdef E_MEMDEBUG
    E_MemName="Material by E3d_MaterialsAllocate";
#endif // E_MEMDEBUG
    if((LMaterial=(E3dMaterial*)EMalloc(sizeof(E3dMaterial)))!=NULL)
    {
     LMaterials[LMCnt]=LMaterial;
     LMaterial->RefCnt=0;
     E3d_MaterialDefault(LMaterial);
    }
   }
  }
  return(LMaterials);
 }
 return(NULL);
}


//========================================================================================
// Free memory associated with a Material
//
// Argument
//  E3dMaterial* LMaterial         Pointer to the Material structure to be freed
//
// Description
//  This function first checks whether the reference count of the Material after
//  decrementing is 0.
//  If not, it means that the Material is still being referenced somewhere, so the
//  function just exits.
//  If the RefCnt is 0 after decrementing, the function will remove the Material from
//  the scene, free its textures and the Material structure itself.
//
// See also
//  E3d_MaterialAllocate, E3d_MaterialDefault
//========================================================================================
void E3d_MaterialFree(E3dMaterial* LMaterial)
{
 E3d2DTexture*	L2DTexture;
 E3d3DTexture*	L3DTexture;
 unsigned int	LC, LN;


 if(LMaterial==(&E3d_DefaultMaterial)) return;
printf("FreeMtl [%s] %08x %d\n", LMaterial->Name, (unsigned int)(LMaterial), LMaterial->RefCnt);fflush(stdout);

//assert(LMaterial->RefCnt!=2);
 if(LMaterial->RefCnt>0)
 {
  LMaterial->RefCnt-=1;
  if(LMaterial->RefCnt) return;
 }

 if(LMaterial->Name) EFree(LMaterial->Name);

 if(LMaterial->Shader) E3d_ShaderFree(LMaterial->Shader);

// Free 2DTextures of the Material
//
 if(LMaterial->Textures2D)
 {
  LN=LMaterial->NumOf2DTextures;
  for(LC=0;LC<LN;LC++)
  {
   if((L2DTexture=LMaterial->Textures2D[LC])!=NULL)
   {
    E3d_2DTextureFree(L2DTexture);
   }
  }
  EFree(LMaterial->Textures2D);
 }

// Free 3D textures of the Material
//
 if(LMaterial->Textures3D)
 {
  LN=LMaterial->NumOf3DTextures;
  for(LC=0;LC<LN;LC++)
  {
   if((L3DTexture=LMaterial->Textures3D[LC])!=NULL)
   {
    E3d_3DTextureFree(L3DTexture);
   }
  }
  EFree(LMaterial->Textures3D);
 }

 EFree(LMaterial);
}


//================================================
// Free an array of Materials
//================================================
void E3d_MaterialsFree(E3dMaterial** LMaterials, unsigned int LCount)
{
 unsigned int	LC;

 for(LC=0;LC<LCount;LC++) E3d_MaterialFree(LMaterials[LC]);
}


#ifdef USEOpenGL
//========================================
// Convert a material to GL format
//========================================
void E3d_MaterialConvertToGL(E3dMaterial* LMaterial)
{
 E3dGLMaterial* LGLMaterial;
 E3d2DTexture*	L2DTexture;
 float		LAmbientR, LAmbientG, LAmbientB,
		LDiffuseR, LDiffuseG, LDiffuseB,
		LSpecularR, LSpecularG, LSpecularB,
		LEmissionR, LEmissionG, LEmissionB;


 LAmbientR=LMaterial->Ambient.R;LAmbientG=LMaterial->Ambient.G;LAmbientB=LMaterial->Ambient.B;
 LDiffuseR=LMaterial->Diffuse.R;LDiffuseG=LMaterial->Diffuse.G;LDiffuseB=LMaterial->Diffuse.B;
 LSpecularR=LMaterial->Specular.R;LSpecularG=LMaterial->Specular.G;LSpecularB=LMaterial->Specular.B;
 LEmissionR=LMaterial->Emission.R;LEmissionG=LMaterial->Emission.G;LEmissionB=LMaterial->Emission.B;


// Always initialize the non-textured GLMaterial, because
// we might be rendering in non-textured mode
//
 LGLMaterial=&(LMaterial->GLMaterial);
 LGLMaterial->Type=LMaterial->Type;

 switch(LMaterial->Type)
 {
  case E3dMAT_BLINN:
  case E3dMAT_PHONG:
   LGLMaterial->Ambient[0]=LAmbientR;LGLMaterial->Ambient[1]=LAmbientG;LGLMaterial->Ambient[2]=LAmbientB;LGLMaterial->Ambient[3]=1.0;
   LGLMaterial->Diffuse[0]=LDiffuseR;LGLMaterial->Diffuse[1]=LDiffuseG;LGLMaterial->Diffuse[2]=LDiffuseB;LGLMaterial->Diffuse[3]=1.0;
   LGLMaterial->Specular[0]=LSpecularR;LGLMaterial->Specular[1]=LSpecularG;LGLMaterial->Specular[2]=LSpecularB;LGLMaterial->Specular[3]=1.0;
   LGLMaterial->Emission[0]=LEmissionR;LGLMaterial->Emission[1]=LEmissionG;LGLMaterial->Emission[2]=LEmissionB;LGLMaterial->Emission[3]=1.0;
   LGLMaterial->Shininess[0]=LMaterial->Specularity;
  break;

  case E3dMAT_LAMBERT:
   LGLMaterial->Ambient[0]=LAmbientR;LGLMaterial->Ambient[1]=LAmbientG;LGLMaterial->Ambient[2]=LAmbientB;LGLMaterial->Ambient[3]=1.0;
   LGLMaterial->Diffuse[0]=LDiffuseR;LGLMaterial->Diffuse[1]=LDiffuseG;LGLMaterial->Diffuse[2]=LDiffuseB;LGLMaterial->Diffuse[3]=1.0;
   LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
   LGLMaterial->Emission[0]=LMaterial->Emission.R;LGLMaterial->Emission[1]=LMaterial->Emission.G;LGLMaterial->Emission[2]=LMaterial->Emission.B;LGLMaterial->Emission[3]=1.0;
   LGLMaterial->Shininess[0]=0.0;
  break;

  case E3dMAT_CONSTANT:
   LGLMaterial->Ambient[0]=LMaterial->Ambient.R;LGLMaterial->Ambient[1]=LMaterial->Ambient.G;LGLMaterial->Ambient[2]=LMaterial->Ambient.B;LGLMaterial->Ambient[3]=1.0;
   LGLMaterial->Diffuse[0]=0.0;LGLMaterial->Diffuse[1]=0.0;LGLMaterial->Diffuse[2]=0.0;LGLMaterial->Diffuse[3]=1.0;
   LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
   LGLMaterial->Emission[0]=0.0;LGLMaterial->Emission[1]=0.0;LGLMaterial->Emission[2]=0.0;LGLMaterial->Emission[3]=1.0;
   LGLMaterial->Shininess[0]=0.0;
  break;

  default:
   LGLMaterial->Ambient[0]=LMaterial->Ambient.R;LGLMaterial->Ambient[1]=LMaterial->Ambient.G;LGLMaterial->Ambient[2]=LMaterial->Ambient.B;LGLMaterial->Ambient[3]=1.0;
   LGLMaterial->Diffuse[0]=0.0;LGLMaterial->Diffuse[1]=0.0;LGLMaterial->Diffuse[2]=0.0;LGLMaterial->Diffuse[3]=1.0;
   LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
   LGLMaterial->Emission[0]=0.0;LGLMaterial->Emission[1]=0.0;LGLMaterial->Emission[2]=0.0;LGLMaterial->Emission[3]=1.0;
   LGLMaterial->Shininess[0]=0.0;
  break;
 }

 LGLMaterial->FlatColor=Ec_FloatsToPackedRGBA((double)(LMaterial->Diffuse.R), (double)(LMaterial->Diffuse.G), (double)(LMaterial->Diffuse.B), (double)(1.0-LMaterial->Transparency));


// Try to approximate the texture blending factors
//
 if(LMaterial->NumOf2DTextures)
 {
  float	LBF, LBFI;

  LGLMaterial=&(LMaterial->GL2DTextureMaterial);
  L2DTexture=LMaterial->Textures2D[0];
  LBF=L2DTexture->AmbientFactor;LBFI=1.0-LBF;
  LAmbientR=LBF+LBFI*LMaterial->Ambient.R;LAmbientG=LBF+LBFI*LMaterial->Ambient.G;LAmbientB=LBF+LBFI*LMaterial->Ambient.B;

  LBF=L2DTexture->DiffuseFactor;LBFI=1.0-LBF;
  LDiffuseR=LBF+LBFI*LMaterial->Diffuse.R;LDiffuseG=LBF+LBFI*LMaterial->Diffuse.G;LDiffuseB=LBF+LBFI*LMaterial->Diffuse.B;

//printf("Diff %f:%f:%f  %f %f\n", LDiffuseR, LDiffuseG, LDiffuseB, LBF, LBFI);fflush(stdout);

  LBF=L2DTexture->SpecularFactor;LBFI=1.0-LBF;
  LSpecularR=LBF+LBFI*LMaterial->Specular.R;LSpecularG=LBF+LBFI*LMaterial->Specular.G;LSpecularB=LBF+LBFI*LMaterial->Specular.B;
/*
  LBF=L2DTexture->EmissionFactor;LBFI=1.0-LBF;
  LEmissionR=LBF+LBFI*LMaterial->Emission.R;LEmissionG=LBF+LBFI*LMaterial->Emission.G;LEmissionB=LBF+LBFI*LMaterial->Emission.B;
*/
  LEmissionR=LMaterial->Emission.R;LEmissionG=LMaterial->Emission.G;LEmissionB=LMaterial->Emission.B;

  switch(LMaterial->Type)
  {
   case E3dMAT_BLINN:
   case E3dMAT_PHONG:
    LGLMaterial->Ambient[0]=LAmbientR;LGLMaterial->Ambient[1]=LAmbientG;LGLMaterial->Ambient[2]=LAmbientB;LGLMaterial->Ambient[3]=1.0;
    LGLMaterial->Diffuse[0]=LDiffuseR;LGLMaterial->Diffuse[1]=LDiffuseG;LGLMaterial->Diffuse[2]=LDiffuseB;LGLMaterial->Diffuse[3]=1.0;
    LGLMaterial->Specular[0]=LSpecularR;LGLMaterial->Specular[1]=LSpecularG;LGLMaterial->Specular[2]=LSpecularB;LGLMaterial->Specular[3]=1.0;
    LGLMaterial->Emission[0]=LEmissionR;LGLMaterial->Emission[1]=LEmissionG;LGLMaterial->Emission[2]=LEmissionB;LGLMaterial->Emission[3]=1.0;
    LGLMaterial->Shininess[0]=LMaterial->Specularity;
   break;

   case E3dMAT_LAMBERT:
    LGLMaterial->Ambient[0]=LAmbientR;LGLMaterial->Ambient[1]=LAmbientG;LGLMaterial->Ambient[2]=LAmbientB;LGLMaterial->Ambient[3]=1.0;
    LGLMaterial->Diffuse[0]=LDiffuseR;LGLMaterial->Diffuse[1]=LDiffuseG;LGLMaterial->Diffuse[2]=LDiffuseB;LGLMaterial->Diffuse[3]=1.0;
    LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
    LGLMaterial->Emission[0]=LMaterial->Emission.R;LGLMaterial->Emission[1]=LMaterial->Emission.G;LGLMaterial->Emission[2]=LMaterial->Emission.B;LGLMaterial->Emission[3]=1.0;
    LGLMaterial->Shininess[0]=0.0;
   break;

   case E3dMAT_CONSTANT:
    LGLMaterial->Ambient[0]=LMaterial->Ambient.R;LGLMaterial->Ambient[1]=LMaterial->Ambient.G;LGLMaterial->Ambient[2]=LMaterial->Ambient.B;LGLMaterial->Ambient[3]=1.0;
    LGLMaterial->Diffuse[0]=0.0;LGLMaterial->Diffuse[1]=0.0;LGLMaterial->Diffuse[2]=0.0;LGLMaterial->Diffuse[3]=1.0;
    LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
    LGLMaterial->Emission[0]=0.0;LGLMaterial->Emission[1]=0.0;LGLMaterial->Emission[2]=0.0;LGLMaterial->Emission[3]=1.0;
    LGLMaterial->Shininess[0]=0.0;
   break;

   default:
    LGLMaterial->Ambient[0]=LMaterial->Ambient.R;LGLMaterial->Ambient[1]=LMaterial->Ambient.G;LGLMaterial->Ambient[2]=LMaterial->Ambient.B;LGLMaterial->Ambient[3]=1.0;
    LGLMaterial->Diffuse[0]=0.0;LGLMaterial->Diffuse[1]=0.0;LGLMaterial->Diffuse[2]=0.0;LGLMaterial->Diffuse[3]=1.0;
    LGLMaterial->Specular[0]=0.0;LGLMaterial->Specular[1]=0.0;LGLMaterial->Specular[2]=0.0;LGLMaterial->Specular[3]=1.0;
    LGLMaterial->Emission[0]=0.0;LGLMaterial->Emission[1]=0.0;LGLMaterial->Emission[2]=0.0;LGLMaterial->Emission[3]=1.0;
    LGLMaterial->Shininess[0]=0.0;
   break;
  }

  LGLMaterial->FlatColor=Ec_FloatsToPackedRGBA((double)(LMaterial->Diffuse.R), (double)(LMaterial->Diffuse.G), (double)(LMaterial->Diffuse.B), (double)1.0);
 }


 if(LMaterial->Transparency>0.0)
 {
  E3dFColorComponent	LOpacity=1.0-LMaterial->Transparency;


  LGLMaterial->Ambient[0]*=LOpacity;
  LGLMaterial->Ambient[1]*=LOpacity;
  LGLMaterial->Ambient[2]*=LOpacity;
  LGLMaterial->Ambient[3]=0.0;

  LGLMaterial->Diffuse[0]*=LOpacity;
  LGLMaterial->Diffuse[1]*=LOpacity;
  LGLMaterial->Diffuse[2]*=LOpacity;

//printf("LOpacity %f\n", LOpacity);fflush(stdout);
  LGLMaterial->Diffuse[3]=LOpacity;

  LGLMaterial->Specular[0]*=LOpacity;
  LGLMaterial->Specular[1]*=LOpacity;
  LGLMaterial->Specular[2]*=LOpacity;
  LGLMaterial->Specular[3]=0.0;

  LGLMaterial->Emission[0]*=LOpacity;
  LGLMaterial->Emission[1]*=LOpacity;
  LGLMaterial->Emission[2]*=LOpacity;
  LGLMaterial->Emission[3]=0.0;

  LGLMaterial->FlatColor=Ec_FloatsToPackedRGBA((double)(LMaterial->Diffuse.R*LOpacity), (double)(LMaterial->Diffuse.G*LOpacity), (double)(LMaterial->Diffuse.B*LOpacity), (double)(1.0-LMaterial->Transparency));
 }
}


//================================================
// Define material for GL
//================================================
void E3d_MaterialDefineGL(E3dGLMaterial* LGLMaterial)
{
 glMaterialfv(GL_FRONT, GL_AMBIENT, LGLMaterial->Ambient);
 glMaterialfv(GL_FRONT, GL_DIFFUSE, LGLMaterial->Diffuse);
 glMaterialfv(GL_FRONT, GL_SPECULAR, LGLMaterial->Specular);
 glMaterialfv(GL_FRONT, GL_EMISSION, LGLMaterial->Emission);
 glMaterialfv(GL_FRONT, GL_SHININESS, LGLMaterial->Shininess);
}
#endif // USEOpenGL



//================================================
// Update a Material for rendering
//================================================
void E3d_MaterialUpdateForDisplay(E3dMaterial* LMaterial)
{
#ifdef USEOpenGL
 E3d_MaterialConvertToGL(LMaterial);
#endif // USEOpenGL
}



//================================================
// Allocate a Shader of a given class
//================================================
E3dShader* E3d_ShaderAllocate(E3dShaderClass* LClass)
{
 E3dShader*	LShader=(E3dShader*)EMalloc(LClass->StructSize);

 LShader->RefCnt=0;
 LShader->Class=LClass;
 return(LShader);
}


//================================================
// Free a Shader instance
//================================================
void E3d_ShaderFree(E3dShader* LShader)
{
 if(LShader->RefCnt)
 {
  LShader->RefCnt-=1;
  if(LShader->RefCnt) return;
 }

 if(LShader->Class->DestroyProc) LShader->Class->DestroyProc(LShader);

 EFree(LShader);
}


//================================================
// Find a ShaderClass by its name
//================================================
E3dShaderClass* E3d_ShaderClassFindByName(char* LName)
{
 E3dShaderClass**	LClasses=E3d_ShaderClasses;

 if(LClasses)
 {
  E3dShaderClass*	LClass;
  unsigned int		LC, LN;

  for(LC=0, LN=E3d_NumOfShaderClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LName)) return(LClass);
  }
 }
 return(NULL);
}


//================================================
// Register a new ShaderClass
//================================================
E3dShaderClass* E3d_ShaderClassRegister(E3dShaderClass* LTemplate)
{
 E3dShaderClass**	LClasses=E3d_ShaderClasses;
 E3dShaderClass*	LClass;
 unsigned int		LC, LN;

// If the class already exists, update it
//
 if(LClasses)
 {
  for(LC=0, LN=E3d_NumOfShaderClasses;LC<LN;LC++)
  {
   LClass=LClasses[LC];
   if(EStr_StringsEqual(LClass->Name, LTemplate->Name))
   {
    LClass->EditProc=LTemplate->EditProc;
    LClass->StructSize=LTemplate->StructSize;
    LClass->SurfaceProc=LTemplate->SurfaceProc;
    LClass->VolumeProc=LTemplate->VolumeProc;
    LClass->GLDrawProc=LTemplate->GLDrawProc;
    LClass->DestroyProc=LTemplate->DestroyProc;
    LClass->ColorsUsed=LTemplate->ColorsUsed;
    LClass->Resources=LTemplate->Resources;

    return(LClass);
   }
  }
 }

 if(LClasses==NULL) E3d_ShaderClasses=LClasses=(E3dShaderClass**)EMalloc(sizeof(E3dShaderClass));
 else
 {
  if((LClasses=(E3dShaderClass**)ERealloc(E3d_ShaderClasses, sizeof(E3dShaderClass*)*(E3d_NumOfShaderClasses+1)))!=NULL) E3d_ShaderClasses=LClasses;
 }

 if(LClasses)
 {
  if((LClass=(E3dShaderClass*)EMalloc(sizeof(E3dShaderClass)))!=NULL)
  {
   LClasses[E3d_NumOfShaderClasses]=LClass;

   LClass->Name=EStrDup(LTemplate->Name);
   LClass->StructSize=LTemplate->StructSize;
   LClass->EditProc=LTemplate->EditProc;
   LClass->SurfaceProc=LTemplate->SurfaceProc;
   LClass->VolumeProc=LTemplate->VolumeProc;
   LClass->GLDrawProc=LTemplate->GLDrawProc;
   LClass->DestroyProc=LTemplate->DestroyProc;
   LClass->ColorsUsed=LTemplate->ColorsUsed;
   LClass->Resources=LTemplate->Resources;


   E3d_NumOfShaderClasses++;
  }
  return(LClass);
 }
 else return(NULL);
}


//================================================
// Register a "blank" Shader class
//================================================
E3dShaderClass* E3d_ShaderClassRegisterInactive(char* LName, unsigned int LStructSize)
{
 E3dShaderClass**	LClasses=E3d_ShaderClasses;
 E3dShaderClass*	LClass;


 if(LClasses==NULL) E3d_ShaderClasses=LClasses=(E3dShaderClass**)EMalloc(sizeof(E3dShaderClass));
 else
 {
  if((LClasses=(E3dShaderClass**)ERealloc(E3d_ShaderClasses, sizeof(E3dShaderClass*)*(E3d_NumOfShaderClasses+1)))!=NULL) E3d_ShaderClasses=LClasses;
 }

 if(LClasses)
 {
  if((LClass=(E3dShaderClass*)EMalloc(sizeof(E3dShaderClass)))!=NULL)
  {
   LClasses[E3d_NumOfShaderClasses]=LClass;

   LClass->Name=EStrDup(LName);
   LClass->StructSize=LStructSize;
   LClass->EditProc=NULL;
   LClass->SurfaceProc=NULL;
   LClass->VolumeProc=NULL;
   LClass->GLDrawProc=NULL;
   LClass->DestroyProc=NULL;
   LClass->Resources=NULL;


   E3d_NumOfShaderClasses++;
  }
  return(LClass);
 }
 else return(NULL);
}


//================================================
// Deactivate a ShaderClass
//================================================
void E3d_ShaderClassDeactivate(E3dShaderClass* LClass)
{
 E3dShaderClass**	LClasses=E3d_ShaderClasses;

 if(LClasses)
 {
  unsigned int		LC, LN;

  for(LC=0, LN=E3d_NumOfShaderClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    LClass->EditProc=NULL;
    LClass->StructSize=0;
    LClass->SurfaceProc=NULL;
    LClass->VolumeProc=NULL;
    LClass->GLDrawProc=NULL;
    LClass->DestroyProc=NULL;
    LClass->Resources=NULL;
   }
  }
 }
}


//================================================
// Remove a Geometry class
//================================================
void E3d_ShaderClassRemove(E3dShaderClass* LClass)
{
 E3dShaderClass**	LClasses=E3d_ShaderClasses;

 if(LClasses)
 {
  unsigned int	LC, LN;

  for(LC=0, LN=E3d_NumOfShaderClasses;LC<LN;LC++)
  {
   if(LClasses[LC]==LClass)
   {
    if(LClass->Name) EFree(LClass->Name);
    EFree(LClass);

    if(LC<(LN-1)) memmove(LClass, LClass+1, sizeof(E3dShaderClass*)*(LN-LC-1));
    E3d_NumOfShaderClasses-=1;
    if(E3d_NumOfShaderClasses==0) { EFree(E3d_ShaderClasses);E3d_ShaderClasses=NULL; }
    else E3d_ShaderClasses=(E3dShaderClass**)ERealloc(E3d_ShaderClasses, sizeof(E3dShaderClass*)*E3d_NumOfShaderClasses);
    return;
   }
  }
 }
}
