/*
 GNU Maverik - a system for managing display and interaction in 
               Virtual Environment applications.
 Copyright (C) 1999 Advanced Interfaces Group

 This program is free software; you can redistribute it and/or
 modify it under the terms of the GNU General Public License
 as published by the Free Software Foundation; either version 2
 of the License, or (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA


 The authors can be contacted via:
 www   - http://aig.cs.man.ac.uk
 email - maverik@aig.cs.man.ac.uk
 mail  - Advanced Interfaces Group, Room 2.90, Computer Science Building, 
         University of Manchester, Manchester, M13 9PL, UK
*/


#include "mavlib_kernel.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
char *strdup (const char *s1);

MAV_surfaceParams mavlib_surfaceParams={-1,-1,-1,-1};
MAV_surfaceParams *mav_sp_current= &mavlib_surfaceParams;
MAV_colour mavlib_colBlack={1,1,{0,0,0,1}};
MAV_colour mavlib_colWhite={1,1,{1,1,1,1}};
MAV_colour mavlib_colRed={1,1,{1,0,0,1}};
MAV_colour mavlib_colGreen={1,1,{0,1,0,1}};
MAV_colour mavlib_colBlue={1,1,{0,0,1,1}};

int mavlib_num_palettes= 0;
int mavlib_use_surface_params= 1;

int mav_opt_maxColours=150;
int mav_opt_maxMaterials=150;
int mav_opt_maxTextures=150;
int mav_opt_maxFonts=10;
int mav_opt_maxLights=5;

/* Routine to enable or disable surface params */

void mav_surfaceParamsFlagSet(int mode)
{
  mavlib_use_surface_params= mode;
}


/* Routine to create a new set of surface params */

MAV_surfaceParams *mav_surfaceParamsNew(int mode, int colour, int material, int texture)
{
  MAV_surfaceParams *rv= mav_malloc(sizeof(MAV_surfaceParams));
  
  rv->mode=mode;
  rv->colour=colour;
  rv->material=material;
  rv->texture=texture;

  return rv;
}



/* Routine to undefine the current used surface params */

void mav_surfaceParamsUndefine(void)
{
  mavlib_surfaceParams.mode=-1;
  mavlib_surfaceParams.colour=-1;
  mavlib_surfaceParams.material=-1;
  mavlib_surfaceParams.texture=-1;
}



/* Routine to use a set of surface params */

void mav_surfaceParamsUse(MAV_surfaceParams *sp)
{
  if (mavlib_use_surface_params) {
    /* return if sp are NULL (keep using current sp's) */
    if (sp==NULL) return;

    /* return if we are already using this set of sp's */
    if (sp->mode==mavlib_surfaceParams.mode && sp->colour==mavlib_surfaceParams.colour &&
        sp->material==mavlib_surfaceParams.material && sp->texture==mavlib_surfaceParams.texture) return;

    /* set to correct mode if we are not already using it */
    if (sp->mode!=mavlib_surfaceParams.mode) {
      mav_gfxColouringModeUse(mav_win_current->palette, sp->mode);
      mavlib_surfaceParams.colour=-1;
      mavlib_surfaceParams.material=-1;
      mavlib_surfaceParams.texture=-1;
    }    

    /* set to correct colour/mat/texture if we are not already using it */
    switch (sp->mode) {
    case MAV_COLOUR:
      if (sp->colour!=mavlib_surfaceParams.colour) {
        if (sp->colour<0) /* build in colours */
          {
  	  switch (sp->colour) {
  	  case MAV_COLOUR_BLACK:
  	    mav_gfxColourUse(mavlib_colBlack);
  	    break;
  	  case MAV_COLOUR_WHITE:
	    mav_gfxColourUse(mavlib_colWhite);
	    break;
	  case MAV_COLOUR_RED:
	    mav_gfxColourUse(mavlib_colRed);
	    break;
	  case MAV_COLOUR_GREEN:
	    mav_gfxColourUse(mavlib_colGreen);
	    break;
	  case MAV_COLOUR_BLUE:
	    mav_gfxColourUse(mavlib_colBlue);
	    break;
	  }
        }
        else
        {
	  mav_gfxColourUse(mav_win_current->palette->collist[sp->colour]);
        }
      }
      break;

    case MAV_MATERIAL:
      if (sp->material!=mavlib_surfaceParams.material) mav_gfxMaterialUse(mav_win_current->palette->matlist[sp->material]);
      break;

    case MAV_TEXTURE:
      if (sp->texture!=mavlib_surfaceParams.texture) mav_gfxTextureUse(mav_win_current->palette->texlist[sp->texture], mav_win_current->palette->texEnv);
      mav_gfxTextureEnv2Set(1);
      mav_gfxColourUse(mavlib_colWhite); /* needed since above is modulated against current fragment colour */
      break;

    case MAV_LIT_TEXTURE:
      if (sp->texture!=mavlib_surfaceParams.texture) mav_gfxTextureUse(mav_win_current->palette->texlist[sp->texture], mav_win_current->palette->texEnv);
      mav_gfxTextureEnv2Set(1);
      if (sp->material!=mavlib_surfaceParams.material) mav_gfxMaterialUse(mav_win_current->palette->matlist[sp->material]);
      break;

    case MAV_BLENDED_TEXTURE:
      if (sp->texture!=mavlib_surfaceParams.texture) mav_gfxTextureUse(mav_win_current->palette->texlist[sp->texture], mav_win_current->palette->texEnv);
      mav_gfxTextureEnv2Set(2);
      if (sp->material!=mavlib_surfaceParams.material) mav_gfxMaterialUse(mav_win_current->palette->matlist[sp->material]);
      break;
    }

    mavlib_surfaceParams= *sp;
  }
}


/* Routine to create a new palette */

MAV_palette *mav_paletteNew(void)
{
  MAV_palette *rv= (MAV_palette *) mav_malloc(sizeof(MAV_palette));
  int i;

 /* 
     Clear colour, material, texture and light defined lists 
     Give each a unique ID to define and bind against.
 */
  
  rv->collist= mav_malloc(mav_opt_maxColours*sizeof(MAV_colour));
  for (i=0; i<mav_opt_maxColours; i++) {
    rv->collist[i].id= (mavlib_num_palettes*mav_opt_maxColours)+i;
    rv->collist[i].defined=0;
  }

  rv->matlist= mav_malloc(mav_opt_maxMaterials*sizeof(MAV_material));
  for (i=0; i<mav_opt_maxMaterials; i++) {
    rv->matlist[i].id= (mavlib_num_palettes*mav_opt_maxMaterials)+i;
    rv->matlist[i].defined=0;
  }

  rv->texlist= mav_malloc(mav_opt_maxTextures*sizeof(MAV_texture));
  for (i=0; i<mav_opt_maxTextures; i++) {
    rv->texlist[i].id= (mavlib_num_palettes*mav_opt_maxTextures)+i;
    rv->texlist[i].defined=0;
    rv->texlist[i].transparent=0;
  }

  rv->lm.id= mavlib_num_palettes;
  rv->lm.defined=0;

  rv->lightlist= mav_malloc(mav_opt_maxLights*sizeof(MAV_light));
  for (i=0; i<mav_opt_maxLights; i++) {
    rv->lightlist[i].id= (mavlib_num_palettes*mav_opt_maxLights)+i;
    rv->lightlist[i].index=i;
    rv->lightlist[i].defined=0;
  }

  rv->fontlist= mav_malloc(mav_opt_maxFonts*sizeof(MAV_font));
  for (i=0; i<mav_opt_maxFonts; i++) {
    rv->fontlist[i].id= (mavlib_num_palettes*mav_opt_maxFonts)+i;
    rv->fontlist[i].defined=0;
  }

  mav_paletteTextureEnvPaletteSet(rv, mav_texEnvDefault);

  mavlib_num_palettes++;

  return rv;
}



/* 
   Routines to call when a light is updated so as the change effects all windows
   which share this palette.
*/

void mavlib_lightUpd(int i, MAV_palette *p)
{
  MAV_window *orig_win= mav_win_current;
  MAV_window *win;

  mav_listPointerReset(mav_win_list);

  while (mav_listItemNext(mav_win_list, (void **) &win)) {
    /* Set in each window which shares this palette */
    if (win->palette==p) {
      if (win!=mav_win_current) mav_windowSet(win);
      mav_gfxLightUse(p->lightlist[i]);
    }
  }

  if (mav_win_current!=orig_win) mav_windowSet(orig_win);

  mav_surfaceParamsUndefine();
}

void mavlib_lightPosUpd(int i, MAV_palette *p)
{
  MAV_window *orig_win= mav_win_current;
  MAV_window *win;

  mav_listPointerReset(mav_win_list);

  while (mav_listItemNext(mav_win_list, (void **) &win)) {
    if (win->palette==p) {
      if (win!=mav_win_current) mav_windowSet(win);
      mav_gfxLightPos(p->lightlist[i]);
    }
  }

  if (mav_win_current!=orig_win) mav_windowSet(orig_win);

  mav_surfaceParamsUndefine();
}

void mavlib_lightingModelUpd(MAV_palette *p)
{
  MAV_window *orig_win= mav_win_current;
  MAV_window *win;
  
  mav_listPointerReset(mav_win_list);
  
  while (mav_listItemNext(mav_win_list, (void **) &win)) {
    if (win->palette==p) {
      if (win!=mav_win_current) mav_windowSet(win);
      mav_gfxLightingModelUse(p->lm);
    }
  }

  if (mav_win_current!=orig_win) mav_windowSet(orig_win);

  mav_surfaceParamsUndefine();
}



/* Routine to set the palette for a given window */

void mav_windowPaletteSet(MAV_window *w, MAV_palette *p)
{
  MAV_window *orig_win= mav_win_current;
  int i;

  w->palette= p;
  if (w==mav_win_all) return;

  /* set to requested window and enable lighting model and lights */
  
  if (orig_win!=w) mav_windowSet(w);  

  mav_gfxLightingModelUse(p->lm);
  for (i=0; i<mav_opt_maxLights; i++) {
    mav_gfxLightUse(p->lightlist[i]);
    mav_gfxLightPos(p->lightlist[i]);
  }

  if (orig_win!=w) mav_windowSet(orig_win);

  mav_surfaceParamsUndefine();  
}



/* Routine to define a palette's texture environment */

void mav_paletteTextureEnvPaletteSet(MAV_palette *p, MAV_texEnvFn fn)
{
  p->texEnv= fn;
}



/* Routine to define an emmisive colour in a palette */

void mav_paletteColourSet(MAV_palette *p, int index, float r, float g, float b, float a)
{
/* Check index is within range */

  if (index > mav_opt_maxColours-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Colour index %i too big (max %i), ignoring\n", index, mav_opt_maxColours-1);
  }
  else
  {

/* Warn if index is already defined, then define it */

    if (p->collist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Colour index %i already defined in palette, overwriting\n", index);
    
    p->collist[index].defined = MAV_REDEFINE_WARN;
    
    p->collist[index].colour[0] = r;
    p->collist[index].colour[1] = g;
    p->collist[index].colour[2] = b;
    p->collist[index].colour[3] = a;
    
    mav_gfxColourSet(p->collist[index]);
    mav_surfaceParamsUndefine();
  }
}



/* Routine to define a material in a palette */

void mav_paletteMaterialSet(MAV_palette *p, int index, float a1, float a2, float a3, float a4,
                                                float d1, float d2, float d3, float d4, 
		                                float s1, float s2, float s3, float s4, 
		                                float e1, float e2, float e3, float e4, float shine)
{
/* Check index is within range */

  if (index > mav_opt_maxMaterials-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Material index %i too big (max %i), ignoring\n", index, mav_opt_maxMaterials-1);
  }
  else
  {

/* Warn if index is already defined, then define it */

    if (p->matlist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Material index %i already defined in palette, overwriting\n", index);
    
    p->matlist[index].defined = MAV_REDEFINE_WARN;
      
    p->matlist[index].ambient[0] = a1;
    p->matlist[index].ambient[1] = a2;
    p->matlist[index].ambient[2] = a3;
    p->matlist[index].ambient[3] = a4;
    
    p->matlist[index].diffuse[0] = d1;
    p->matlist[index].diffuse[1] = d2; 
    p->matlist[index].diffuse[2] = d3;
    p->matlist[index].diffuse[3] = d4;
    
    p->matlist[index].specular[0] = s1;
    p->matlist[index].specular[1] = s2;
    p->matlist[index].specular[2] = s3;
    p->matlist[index].specular[3] = s4;
    
    p->matlist[index].emission[0] = e1;
    p->matlist[index].emission[1] = e2;
    p->matlist[index].emission[2] = e3;
    p->matlist[index].emission[3] = e4;
    
    p->matlist[index].shine=shine;
    
    mav_gfxMaterialSet(p->matlist[index]);
    mav_surfaceParamsUndefine();
  }
}

#ifdef MAV_TIFF
#include <tiffio.h>
#endif

/* Routine to read a texture from a tiff file */

int mavlib_readTiff(char *filename, int *width, int *height, unsigned long **mem)
{
#ifdef MAV_TIFF
  TIFF *tif;
  TIFFRGBAImage img;
  unsigned long *map;
  char emsg[1024];

/* Open tiff file */

  tif = TIFFOpen(filename, "r");
  if (tif == NULL) return(MAV_FALSE);

/* Parse */

  if (TIFFRGBAImageBegin(&img, tif, 0, emsg))
    {
      *width  = (int) img.width;
      *height = (int) img.height;
      *mem = mav_malloc(*width**height * sizeof(unsigned long));
      map=*mem;

      if (TIFFRGBAImageGet(&img, map, img.width, img.height) == 0) return(MAV_FALSE);

      /* TIFF library read in ABGR. However, Linux needs a byte-order swap */

#ifdef MAV_LINUX
      {
        int i,b1,b2,b3,b4;

        for (i=0; i<*height**width; i++) {
	  b1=(int) (map[i] & 0xff);
	  b2=(int) ((map[i] >> 8) & 0xff);
	  b3=(int) ((map[i] >> 16) & 0xff);
	  b4=(int) ((map[i] >> 24) & 0xff);
	  
	  map[i]= (b1<<24) + (b2<<16) + (b3<<8) + b4;
        }
      }
#endif
      TIFFRGBAImageEnd(&img);
    }
  else 
    {
      return(MAV_FALSE);
    }

  return(MAV_TRUE);
#else
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: TIFF format not supported on this platform, ignoring\n");
  return(MAV_FALSE);
#endif
}



/* Routine to read a texture from a raw or ascii PPM file */

int mavlib_readPPM(char *filename, int *width, int *height, unsigned long **mem)
{
  FILE *file;
  float rf, gf, bf;
  int r, g, b, i, j, mv;
  char str[100];
  unsigned long *map;
  int bin=0;

/* Open PPM file */

  file=fopen(filename, "r");
  if (!file) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: can not read texture file %s, ignoring\n", filename);
    return(MAV_FALSE);
  }

/* Check magic number */
      
  fscanf(file, "%s", str);

  if (strcmp(str, "P3") && strcmp(str, "P6")) {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: bad magic number %s (expecting P3 or P6), ignoring\n", str);
    return(MAV_FALSE);
  }
  
  if (!strcmp(str, "P6")) bin=1;

  fscanf(file, "%s", str);
  while (str[0]=='#') { /* comment - read to end of line */
    while (str[0]!=10) fscanf(file, "%c", &str[0]);
    fscanf(file, "%s", str);
  }
  
  *width= atoi(str);
  
  fscanf(file, "%s", str);
  while (str[0]=='#') { /* comment - read to end of line */
    while (str[0]!=10) fscanf(file, "%c", &str[0]);
    fscanf(file, "%s", str);
  }
  
  *height= atoi(str);

  fscanf(file, "%i", &mv);

  /* if binary, read since white space char */
  if (bin) fscanf(file, "%c", &str[0]);

/* Parse */
  
  *mem=(unsigned long *) mav_malloc(*width**height*sizeof(unsigned long));
  map=*mem;

  for (i=*height-1; i>=0; i--) {
    for (j=0; j<*width; j++) {	  

      if (bin) 
      {
	fscanf(file, "%c%c%c", &str[0], &str[1], &str[2]);
	r= str[0];
	g= str[1];
	b= str[2];
      }
      else
      {
	fscanf(file, "%i %i %i", &r, &g, &b);
      }

      rf=((float) r)/mv;
      gf=((float) g)/mv;
      bf=((float) b)/mv;

#ifdef MAV_LINUX
      map[i**width+j]=(((int) (rf*255))<<24) + (((int) (gf*255))<<16) + (((int) (bf*255))<<8) + 255;
#else
      map[i**width+j]=(255<<24) + (((int) (bf*255))<<16) + (((int) (gf*255))<<8) + (((int) (rf*255)));
#endif
    }
  }

  fclose(file);

  return(MAV_TRUE);
}



/* Routine to define a texture in a given palette (from file) */

#define L2 0.301029995

int mav_paletteTextureSet(MAV_palette *p, int index, char *filename)
{
  int width, height, rv= MAV_FALSE;

/* Check index is within range */

  if (index > mav_opt_maxTextures-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Texture index %i too big (max %i), ignoring\n", index, mav_opt_maxTextures-1);
  }
  else
  {
    
    /* Warn if index is already defined */
    
    if (p->texlist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Texture index %i already defined in palette, overwriting\n", index);
    
    /* Use appropriate read for file extension type */
    
    if (strstr(filename, ".pnm") || strstr(filename, ".ppm")) rv= mavlib_readPPM(filename, &p->texlist[index].width, &p->texlist[index].height, &p->texlist[index].mem);
    else if (strstr(filename, ".tif") || strstr(filename, ".tiff")) rv= mavlib_readTiff(filename, &p->texlist[index].width, &p->texlist[index].height, &p->texlist[index].mem);
    else {
      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: file format for %s not recognised, ignoring\n", filename);
    }
    
    /* Only interger powers of 2 allowed for height and width */
    
    if (rv) {      
      width= p->texlist[index].width;
      height= p->texlist[index].height;
      
      if (fabs(log10(width)/L2 - (int) (log10(width)/L2))>0.0001 || 
	  fabs(log10(height)/L2 - (int) (log10(height)/L2))>0.0001) {
	if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: width and height (%ix%i) must be an integer power of 2, ignoring\n", width, height);
	rv=MAV_FALSE;
      }
    }
    
    /* set defined flag */
    
    if (rv) {
      p->texlist[index].defined=MAV_REDEFINE_WARN;
      p->texlist[index].filename= strdup(filename);
      p->texlist[index].texEnv= NULL;
      
      mav_gfxTextureSet(p->texlist[index], p->texEnv);
      mav_surfaceParamsUndefine();
    }
  }
  
  return(rv);
}



/* Routine to set the alpha component of a texture to a given value */

void mav_paletteTextureAlphaSet(MAV_palette *p, int index, float a)
{
  MAV_texture *tex;
  int i, v;

  /* Clamp alpha value and turn into an integer */
  if (a>1.0) a=1.0;
  if (a<0.0) a=0.0;
  v=255*a;

  /* Check index is within range */

  if (index > mav_opt_maxTextures-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Texture index %i too big (max %i), ignoring\n", index, mav_opt_maxTextures-1);
  }
  else
  {
      
    /* Warn if index is not already defined, then define it */
    
    if (!p->texlist[index].defined && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Texture index %i not defined in palette, overwriting\n", index);

    tex= &p->texlist[index];
    for (i=0; i<tex->width*tex->height; i++) {
#ifdef MAV_LINUX
      tex->mem[i]= (tex->mem[i]&0xffffff00) + v;
#else
      tex->mem[i]= (tex->mem[i]&0x00ffffff) + (v<<24);
#endif		
    }

    /* set transparency flag */
    if (v!=255) 
    {
      tex->transparent=1;
    }
    else
    {
      tex->transparent=0;
    }
    
    mav_gfxTextureSet(p->texlist[index], p->texEnv);
    mav_surfaceParamsUndefine();
  }
}



/* Routine to set the alpha component of a texture whose pixels are coloured rgb */

void mav_paletteTextureColourAlphaSet(MAV_palette *p, int index, int r, int g, int b, float a)
{
  MAV_texture *tex;
  int i, v;

  /* Clamp alpha value and turn into an integer */
  if (a>1.0) a=1.0;
  if (a<0.0) a=0.0;
  v=255*a;

  /* Check index is within range */
  
  if (index > mav_opt_maxTextures-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Texture index %i too big (max %i), ignoring\n", index, mav_opt_maxTextures-1);
  }
  else
  {

/* Warn if index is not already defined, then define it */
    
    if (!p->texlist[index].defined && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Texture index %i not defined in palette, overwriting\n", index);
    
    tex= &p->texlist[index];
    for (i=0; i<tex->width*tex->height; i++) {
#ifdef MAV_LINUX
      if (((tex->mem[i]>>24)&0xff)==r && ((tex->mem[i]>>16)&0xff)==g && ((tex->mem[i]>>8)&0xff)==b) tex->mem[i]= (tex->mem[i]&0xffffff00) + v;
#else
      if ((tex->mem[i]&0xff)==r && ((tex->mem[i]>>8)&0xff)==g && ((tex->mem[i]>>16)&0xff)==b) tex->mem[i]= (tex->mem[i]&0x00ffffff) + (v<<24);
#endif		
    }

    /* set transparency flag */
    if (v!=255) 
    {
      tex->transparent=1;
    }
    else
    {
      tex->transparent=0;
    }
    
    mav_gfxTextureSet(p->texlist[index], p->texEnv);
    mav_surfaceParamsUndefine();
  }
}



/* Routine to define a texture in a given palette (from mem) */

int mav_paletteTextureSetFromMem(MAV_palette *p, int index, int width, int height, unsigned long *mem)
{
  int rv= MAV_TRUE;

/* Check index is within range */

  if (index > mav_opt_maxTextures-1)
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Texture index %i too big (max %i), ignoring\n", index, mav_opt_maxTextures-1);
    rv= MAV_FALSE;
  }
  else
  {
    
    /* Warn if index is already defined */
    
    if (p->texlist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Texture index %i already defined in palette, overwriting\n", index);

    /* Only interger powers of 2 allowed for height and width */

    if (fabs(log10(width)/L2 - (int) (log10(width)/L2))>0.0001 || 
	fabs(log10(height)/L2 - (int) (log10(height)/L2))>0.0001) {
      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: width and height (%ix%i) must be an integer power of 2, ignoring\n", width, height);
      rv=MAV_FALSE;
    }
  
    if (rv) {
      p->texlist[index].defined=MAV_REDEFINE_WARN;
      p->texlist[index].filename= strdup("from memory");
      p->texlist[index].texEnv= NULL;
      p->texlist[index].width= width;
      p->texlist[index].height= height;
      p->texlist[index].mem= mem;
      
      mav_gfxTextureSet(p->texlist[index], p->texEnv);
      mav_surfaceParamsUndefine();
    }
  }
  
  return rv;
}



/* Define texture environment */

int mav_paletteTextureEnvSet(MAV_palette *p, int index, MAV_texEnvFn fn)
{
  int rv= MAV_TRUE;

/* Check index is within range */

  if (index > mav_opt_maxTextures-1)
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Texture index %i too big (max %i), ignoring\n", index, mav_opt_maxTextures-1);
    rv= MAV_FALSE;
  }
  else
  {
    /* Warn if index is not already defined, then define it */
    
    if (!p->texlist[index].defined && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Texture index %i not defined in palette, overwriting\n", index);
    
    p->texlist[index].texEnv= fn;
  }

  return rv;
}



/* Default texture environment */

void mav_texEnvDefault(void)
{
  mav_gfxTextureEnv1Set(1);
}



/* Routine to free a texture */

void mav_paletteTextureFree(MAV_palette *p, int index)
{
  if (p->texlist[index].defined) {
    if (p->texlist[index].filename) mav_free(p->texlist[index].filename);
    if (p->texlist[index].mem) mav_free(p->texlist[index].mem);
    p->texlist[index].defined= MAV_FALSE;
  }
}



/* Routine to define a light in a given palette */

void mav_paletteLightSet(MAV_palette *p, int index, float a1, float a2, float a3, float a4, 
		                             float d1, float d2, float d3, float d4,
		                             float s1, float s2, float s3, float s4)
{
  /* Check index is within range */
  
  if (index > mav_opt_maxLights-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Light index %i too big (max %i), ignoring\n", index, mav_opt_maxLights-1);
  }
  else
  {
      
/* Warn if index is already defined, then define it */

    if (p->lightlist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Light index %i already defined in palette, overwriting\n", index);
    
    p->lightlist[index].defined= MAV_REDEFINE_WARN;
    p->lightlist[index].index= index;

    p->lightlist[index].ambient[0]=a1;
    p->lightlist[index].ambient[1]=a2;
    p->lightlist[index].ambient[2]=a3;
    p->lightlist[index].ambient[3]=a4;
      
    p->lightlist[index].diffuse[0]=d1;
    p->lightlist[index].diffuse[1]=d2;
    p->lightlist[index].diffuse[2]=d3;
    p->lightlist[index].diffuse[3]=d4;
    
    p->lightlist[index].specular[0]=s1;
    p->lightlist[index].specular[1]=s2;
    p->lightlist[index].specular[2]=s3;
    p->lightlist[index].specular[3]=s4;

    mav_gfxLightSet(p->lightlist[index]);
    /* set changes in all windows with this palette */
    mavlib_lightUpd(index, p);
  }
}



/* Routine to position a light. N.B. pos is multipled by current view matrix */

void mav_paletteLightPos(MAV_palette *p, int index, MAV_vector pos) 
{
  /* Check index is within range */
  
  if (index >= mav_opt_maxLights) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Light index %i too big (max %i), ignoring\n", index, mav_opt_maxLights-1);
  }
  else
  {
    /* Warn if index is not already defined */
    
    if (!p->lightlist[index].defined && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Light index %i not defined in palette, overwriting\n", index);
    
    p->lightlist[index].pos= pos;    

    /* set changes in all windows with this palette */
    mavlib_lightPosUpd(index, p);
  }
}



/* Routine to define the lighting model for a given palette */

void mav_paletteLightingModelSet(MAV_palette *p, float a1, float a2, float a3, float a4, int local_viewer)
{
  if (p->lm.defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Lighting model already defined in palette, overwriting\n");

  p->lm.defined=MAV_REDEFINE_WARN;
  p->lm.ambient[0]=a1;
  p->lm.ambient[1]=a2;
  p->lm.ambient[2]=a3;
  p->lm.ambient[3]=a4;
  p->lm.localviewer=local_viewer;

  mav_gfxLightingModelSet(p->lm);
  /* set changes in all windows with this palette */
  mavlib_lightingModelUpd(p);
}



/* Routine to check if surface params are transparent */

int mav_surfaceParamsIsTransparent(MAV_window *win, MAV_surfaceParams *sp)
{
  int rv= MAV_FALSE;

  if (sp==NULL) return (mav_surfaceParamsIsTransparent(win, mav_sp_current));

  switch (sp->mode) {
  case MAV_COLOUR:
    if (win->palette->collist[sp->colour].colour[3]<0.99) rv= MAV_TRUE;
    break;
  case MAV_MATERIAL:
    if (win->palette->matlist[sp->material].ambient[3]<0.99 ||
	win->palette->matlist[sp->material].diffuse[3]<0.99 ||
	win->palette->matlist[sp->material].specular[3]<0.99 ||
	win->palette->matlist[sp->material].emission[3]<0.99) rv= MAV_TRUE;
    break;
  case MAV_TEXTURE:
    if (win->palette->texlist[sp->texture].transparent) rv= MAV_TRUE;
    break;
  case MAV_LIT_TEXTURE:
  case MAV_BLENDED_TEXTURE:
    if (win->palette->texlist[sp->texture].transparent) rv= MAV_TRUE;
    if (win->palette->matlist[sp->material].ambient[3]<0.99 ||
	win->palette->matlist[sp->material].diffuse[3]<0.99 ||
	win->palette->matlist[sp->material].specular[3]<0.99 ||
	win->palette->matlist[sp->material].emission[3]<0.99) rv= MAV_TRUE;
    break;
  }

  return rv;
}



/* Routine to check if surface params are textured */

int mav_surfaceParamsIsTextured(MAV_window *win, MAV_surfaceParams *sp)
{
  int rv= MAV_FALSE;

  if (sp==NULL) return (mav_surfaceParamsIsTextured(win, mav_sp_current));

  if (sp->mode>=MAV_TEXTURE) rv= MAV_TRUE;

  return rv;
}



/* Routine to define a font */

void mav_paletteFontSet(MAV_palette *p, int index, char *s)
{
/* Check index is within range */

  if (index > mav_opt_maxFonts-1) 
  {
    if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: Font index %i too big (max %i), ignoring\n", index, mav_opt_maxFonts-1);
  }
  else
  {

/* Warn if index is already defined, then define it */

    if (p->fontlist[index].defined==MAV_REDEFINE_WARN && mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: Font index %i already defined in palette, overwriting\n", index);

    if (mav_gfxWindowFontSet(s, index, p->fontlist[index].width)!=0) 
    {
      if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Error: can not find font %s, ignoring\n", s);
    }
    else
    {
      p->fontlist[index].name= strdup(s);
      p->fontlist[index].defined = MAV_REDEFINE_WARN;
    }
  }
}



/* Routines to return an empty index in the palette */

int mav_paletteColourIndexEmptyGet(MAV_palette *p)
{
  int i;

  for (i=0; i<mav_opt_maxColours; i++) if (p->collist[i].defined==MAV_FALSE) return i;
  
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find empty colour index in palette\n");  

  return -1;
}

int mav_paletteMaterialIndexEmptyGet(MAV_palette *p)
{
  int i;

  for (i=0; i<mav_opt_maxMaterials; i++) if (p->matlist[i].defined==MAV_FALSE) return i;
  
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find empty material index in palette\n");  

  return -1;
}

int mav_paletteTextureIndexEmptyGet(MAV_palette *p)
{
  int i;

  for (i=0; i<mav_opt_maxTextures; i++) if (p->texlist[i].defined==MAV_FALSE) return i;
  
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find empty texture index in palette\n");  

  return -1;
}

int mav_paletteLightIndexEmptyGet(MAV_palette *p)
{
  int i;

  for (i=0; i<mav_opt_maxLights; i++) if (p->lightlist[i].defined==MAV_FALSE) return i;
  
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find empty light index in palette\n");  

  return -1;
}

int mav_paletteFontIndexEmptyGet(MAV_palette *p)
{
  int i;

  for (i=0; i<mav_opt_maxFonts; i++) if (p->fontlist[i].defined==MAV_FALSE) return i;
  
  if (mav_opt_output==MAV_VERBOSE) fprintf(stderr, "Warning: can not find empty font index in palette\n");  

  return -1;
}



/* Routines to return a matching index in the palette */

int mav_paletteColourIndexMatchGet(MAV_palette *p, float r, float g, float b, float a)
{
  int i;
  
  for (i=0; i<mav_opt_maxColours; i++) {
    if (p->collist[i].defined && fabs(p->collist[i].colour[0]-r)<0.01 && 
	fabs(p->collist[i].colour[1]-g)<0.01 && fabs(p->collist[i].colour[2]-b) <0.01 && 
	fabs(p->collist[i].colour[3]-a)<0.01) return i;
  }

  return -1;
}

int mav_paletteMaterialIndexMatchGet(MAV_palette *p, float ar, float ag, float ab, float aa, 
                                     float dr, float dg, float db, float da, float sr, float sg, float sb, float sa, 
                                     float er, float eg, float eb, float ea, float shin)
{
  int i;
  
  for (i=0; i<mav_opt_maxMaterials; i++) {
    if (p->matlist[i].defined && 
	fabs(p->matlist[i].ambient[0]-ar)<0.01 && fabs(p->matlist[i].ambient[1]-ag)<0.01 && 
	fabs(p->matlist[i].ambient[2]-ab)<0.01 && fabs(p->matlist[i].ambient[3]-aa)<0.01 && 
	fabs(p->matlist[i].diffuse[0]-dr)<0.01 && fabs(p->matlist[i].diffuse[1]-dg)<0.01 && 
	fabs(p->matlist[i].diffuse[2]-db)<0.01 && fabs(p->matlist[i].diffuse[3]-da)<0.01 && 
	fabs(p->matlist[i].specular[0]-sr)<0.01 && fabs(p->matlist[i].specular[1]-sg)<0.01 && 
	fabs(p->matlist[i].specular[2]-sb)<0.01 && fabs(p->matlist[i].specular[3]-sa)<0.01 && 
	fabs(p->matlist[i].emission[0]-er)<0.01 && fabs(p->matlist[i].emission[1]-eg)<0.01 && 
	fabs(p->matlist[i].emission[2]-eb)<0.01 && fabs(p->matlist[i].emission[3]-ea)<0.01 && 
	fabs(p->matlist[i].shine-shin)<0.01) return i;
  }

  return -1;
}

int mav_paletteTextureIndexMatchGet(MAV_palette *p, char *filename)
{
  int i;
  
  for (i=0; i<mav_opt_maxTextures; i++) {
    if (p->texlist[i].defined && p->texlist[i].filename && !strcmp(p->texlist[i].filename, filename)) return i;
  }

  return -1;
}

int mav_paletteLightIndexMatchGet(MAV_palette *p, float ar, float ag, float ab, float aa, 
		                  float dr, float dg, float db, float da, float sr, float sg, float sb, float sa)
{
  int i;
  
  for (i=0; i<mav_opt_maxLights; i++) {
    if (p->lightlist[i].defined && 
	fabs(p->lightlist[i].ambient[0]-ar)<0.01 && fabs(p->lightlist[i].ambient[1]-ag)<0.01 &&
	fabs(p->lightlist[i].ambient[2]-ab)<0.01 && fabs(p->lightlist[i].ambient[3]-aa)<0.01 &&
	fabs(p->lightlist[i].diffuse[0]-dr)<0.01 && fabs(p->lightlist[i].diffuse[1]-dg)<0.01 &&
	fabs(p->lightlist[i].diffuse[2]-db)<0.01 && fabs(p->lightlist[i].diffuse[3]-da)<0.01 &&
	fabs(p->lightlist[i].specular[0]-sr)<0.01 && fabs(p->lightlist[i].specular[1]-sg)<0.01 &&
	fabs(p->lightlist[i].specular[2]-sb)<0.01 && fabs(p->lightlist[i].specular[3]-sa)<0.01) return i;
  }

  return -1;
}

int mav_paletteFontIndexMatchGet(MAV_palette *p, char *s)
{
  int i;
  
  for (i=0; i<mav_opt_maxFonts; i++) {
    if (p->fontlist[i].defined && p->fontlist[i].name && !strcmp(p->fontlist[i].name, s)) return i;
  }

  return -1;
}
