/* light.c */

/*
 * Mesa 3-D graphics library
 * Version:  1.2
 * Copyright (C) 1995  Brian Paul  (brianp@ssec.wisc.edu)
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Library General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library 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
 * Library General Public License for more details.
 *
 * You should have received a copy of the GNU Library General Public
 * License along with this library; if not, write to the Free
 * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 */


/*
$Id: light.c,v 1.22 1995/10/06 13:02:56 brianp Exp $

$Log: light.c,v $
 * Revision 1.22  1995/10/06  13:02:56  brianp
 * fixed glMaterial GL_AMBIENT_AND_DIFFUSE bug
 *
 * Revision 1.21  1995/09/26  17:20:33  brianp
 * fixed uninitialized fmask,bmask bug in gl_material
 *
 * Revision 1.20  1995/09/25  19:24:19  brianp
 * implemented per-vertex glMaterial calls
 *
 * Revision 1.19  1995/09/25  13:21:13  brianp
 * fixed a bug in computing the specular coefficient per Olaf Flebbe
 *
 * Revision 1.18  1995/09/22  16:22:03  brianp
 * added comment about glMaterial not working inside glBegin/glEnd
 *
 * Revision 1.17  1995/09/05  15:35:08  brianp
 * introduced CC.NewState convention
 * many small optimizations made in gl_color_shade() and gl_index_shade()
 *
 * Revision 1.16  1995/07/25  13:27:51  brianp
 * gl_index_shade() returns GLfloats instead of GLuints
 *
 * Revision 1.15  1995/05/30  15:10:45  brianp
 * use ROUND() macro in glGetMaterialiv
 *
 * Revision 1.14  1995/05/29  21:21:53  brianp
 * added glGetMaterial*() functions
 *
 * Revision 1.13  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.12  1995/05/12  17:00:43  brianp
 * changed CC.Mode!=0 to INSIDE_BEGIN_END
 *
 * Revision 1.11  1995/05/12  16:29:51  brianp
 * fixed glGetLightiv()'s prototype
 *
 * Revision 1.10  1995/04/13  19:48:33  brianp
 * fixed GL_LIGHT_MODEL_LOCAL_VIEWER bugs per Armin Liebchen
 *
 * Revision 1.9  1995/04/08  15:25:45  brianp
 * compile glShadeModel
 * fixed pow() domain error
 * added spotlight and attenutation factor to gl_index_shade
 *
 * Revision 1.8  1995/03/28  20:28:03  brianp
 * fixed alpha lighting bug
 *
 * Revision 1.7  1995/03/10  21:41:01  brianp
 * added divide by zero checks
 *
 * Revision 1.6  1995/03/09  21:41:03  brianp
 * new ModelViewInv matrix logic
 *
 * Revision 1.5  1995/03/09  20:07:08  brianp
 * changed gl_transform_point to macro call
 * changed order of arguments in gl_transform_vector
 *
 * Revision 1.4  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.3  1995/02/25  21:01:56  brianp
 * added underflow check for specular coefficient
 *
 * Revision 1.2  1995/02/25  18:52:12  brianp
 * changed NORMALIZE macro
 *
 * Revision 1.1  1995/02/24  14:23:06  brianp
 * Initial revision
 *
 */


#include <assert.h>
#include <math.h>
#ifndef M_PI
#  define M_PI (3.1415926)
#endif
#include "context.h"
#include "list.h"
#include "macros.h"
#include "vb.h"
#include "xform.h"



#define RAD2DEG (180.0/M_PI)




void glShadeModel( GLenum mode )
{
   if (CC.CompileFlag) {
      gl_save_shademodel( mode );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glShadeModel" );
	 return;
      }

      switch (mode) {
	 case GL_FLAT:
	 case GL_SMOOTH:
	    CC.Light.ShadeModel = mode;
            CC.NewState = GL_TRUE;
	    break;
	 default:
	    gl_error( GL_INVALID_ENUM, "glShadeModel" );
      }
   }
}




void glColorMaterial( GLenum face, GLenum mode )
{
   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glColorMaterial" );
      return;
   }

   switch (face) {
      case GL_FRONT:
      case GL_BACK:
      case GL_FRONT_AND_BACK:
         if (CC.ExecuteFlag) {
	    CC.Light.ColorMaterialFace = face;
	 }
	 if (CC.CompileFlag) {
	    gl_save_set_enum( &CC.Light.ColorMaterialFace, face );
	 }
	 break;
      default:
	 gl_error( GL_INVALID_ENUM, "glColorMaterial" );
	 return;
   }

   switch (mode) {
      case GL_EMISSION:
      case GL_AMBIENT:
      case GL_DIFFUSE:
      case GL_SPECULAR:
      case GL_AMBIENT_AND_DIFFUSE:
         if (CC.ExecuteFlag) {
	    CC.Light.ColorMaterialMode = mode;
	 }
	 if (CC.CompileFlag) {
	    gl_save_set_enum( &CC.Light.ColorMaterialMode, mode );
	 }
	 break;
      default:
	 gl_error( GL_INVALID_ENUM, "glColorMaterial" );
	 return;
   }
}




/*
 * pname: GL_AMBIENT, red, green, blue, alpha in [-1,1]
 *        GL_DIFFUSE, red, green, blue, alhpa in [-1,1]
 *        GL_SPECULAR, red, green, blue, alpha in [-1,1]
 *        GL_POSITION, x, y, z, w in homogeneous object coordinates
 *        GL_SPOT_DIRECTION, dx, dy, dz
 *        GL_SPOT_EXPONENT, intensity in [0,128]
 *        GL_SPOT_CUTOFF, angle in [0,90] or 180
 *        GL_CONSTANT_ATTENUATION, factor >= 0
 *        GL_LINEAR_ATTENUATION, factor >= 0
 *        GL_QUADRATIC_ATTENUATION, factor >= 0
 */
void gl_light( GLenum light, GLenum pname, const GLfloat *params )
{
   GLint l;

   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glShadeModel" );
      return;
   }

   l = (GLint) (light - GL_LIGHT0);

   if (l<0 || l>=MAX_LIGHTS) {
      gl_error( GL_INVALID_ENUM, "glLight" );
      return;
   }

   switch (pname) {
      case GL_AMBIENT:
         COPY_4V( CC.Light.Light[l].Ambient, params );
         break;
      case GL_DIFFUSE:
         COPY_4V( CC.Light.Light[l].Diffuse, params );
         break;
      case GL_SPECULAR:
         COPY_4V( CC.Light.Light[l].Specular, params );
         break;
      case GL_POSITION:
	 /* transform position by ModelView matrix */
	 TRANSFORM_POINT( CC.Light.Light[l].Position, CC.ModelViewMatrix, params );
         COPY_3V( CC.Light.Light[l].NormPosition, CC.Light.Light[l].Position );
         NORMALIZE_3V( CC.Light.Light[l].NormPosition );
         break;
      case GL_SPOT_DIRECTION:
	 /* transform direction by inverse modelview */
	 if (!CC.ModelViewInvValid) {
	    gl_compute_modelview_inverse();
	 }
	 gl_transform_vector( CC.Light.Light[l].Direction,
			      params, CC.ModelViewInv);
         break;
      case GL_SPOT_EXPONENT:
         if (params[0]<0.0 || params[0]>128.0) {
            gl_error( GL_INVALID_VALUE, "glLight" );
            return;
         }
         CC.Light.Light[l].SpotExponent = params[0];
         break;
      case GL_SPOT_CUTOFF:
         if ((params[0]<0.0 || params[0]>90.0) && params[0]!=180.0) {
            gl_error( GL_INVALID_VALUE, "glLight" );
            return;
         }
         CC.Light.Light[l].SpotCutoff = params[0];
         break;
      case GL_CONSTANT_ATTENUATION:
         if (params[0]<0.0) {
            gl_error( GL_INVALID_VALUE, "glLight" );
            return;
         }
         CC.Light.Light[l].ConstantAttenuation = params[0];
         break;
      case GL_LINEAR_ATTENUATION:
         if (params[0]<0.0) {
            gl_error( GL_INVALID_VALUE, "glLight" );
            return;
         }
         CC.Light.Light[l].LinearAttenuation = params[0];
         break;
      case GL_QUADRATIC_ATTENUATION:
         if (params[0]<0.0) {
            gl_error( GL_INVALID_VALUE, "glLight" );
            return;
         }
         CC.Light.Light[l].QuadraticAttenuation = params[0];
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glLight" );
         break;
   }

}



/*
 * light: GL_LIGHT0, GL_LIGHT1, ... GL_LIGHTn where n=GL_MAX_LIGHTS-1
 * pname: GL_SPOT_EXPONENT, GL_SPOT_CUTOFF, GL_CONSTANT_ATTENUATION,
 *        GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION
 */
void glLightf( GLenum light, GLenum pname, GLfloat param )
{
   if (CC.ExecuteFlag) {
      gl_light( light, pname, &param );
   }
   if (CC.CompileFlag) {
      gl_save_light( light, pname, &param, 1 );
   }
}



void glLighti( GLenum light, GLenum pname, GLint param )
{
   GLfloat fparam;

   fparam = (GLfloat) param;
   if (CC.ExecuteFlag) {
      gl_light( light, pname, &fparam );
   }
   if (CC.CompileFlag) {
      gl_save_light( light, pname, &fparam, 1 );
   }
}



void glLightfv( GLenum light, GLenum pname, const GLfloat *params )
{
   if (CC.ExecuteFlag) {
      gl_light( light, pname, params );
   }
   if (CC.CompileFlag) {
      gl_save_light( light, pname, params, 4 );
   }
}



void glLightiv( GLenum light, GLenum pname, const GLint *params )
{
   GLfloat fparam[4];

   switch (pname) {
      case GL_AMBIENT:
      case GL_DIFFUSE:
      case GL_SPECULAR:
         fparam[0] = INT_TO_FLOAT( params[0] );
         fparam[1] = INT_TO_FLOAT( params[1] );
         fparam[2] = INT_TO_FLOAT( params[2] );
         fparam[3] = INT_TO_FLOAT( params[3] );
         break;
      case GL_POSITION:
         fparam[0] = (GLfloat) params[0];
         fparam[1] = (GLfloat) params[1];
         fparam[2] = (GLfloat) params[2];
         fparam[3] = (GLfloat) params[3];
         break;
      case GL_SPOT_DIRECTION:
         fparam[0] = (GLfloat) params[0];
         fparam[1] = (GLfloat) params[1];
         fparam[2] = (GLfloat) params[2];
         break;
      case GL_SPOT_EXPONENT:
      case GL_SPOT_CUTOFF:
      case GL_CONSTANT_ATTENUATION:
      case GL_LINEAR_ATTENUATION:
      case GL_QUADRATIC_ATTENUATION:
         fparam[0] = (GLfloat) params[0];
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glLight" );
         return;
   }
   if (CC.ExecuteFlag) {
      gl_light( light, pname, fparam );
   }
   if (CC.CompileFlag) {
      gl_save_light( light, pname, fparam, 4 );
   }
}



void glGetLightfv( GLenum light, GLenum pname, GLfloat *params )
{
   GLint l;

   l = (GLint) (light - GL_LIGHT0);

   if (l<0 || l>=MAX_LIGHTS) {
      gl_error( GL_INVALID_ENUM, "glGetLightfv" );
      return;
   }

   switch (pname) {
      case GL_AMBIENT:
         COPY_4V( params, CC.Light.Light[l].Ambient );
         break;
      case GL_DIFFUSE:
         COPY_4V( params, CC.Light.Light[l].Diffuse );
         break;
      case GL_SPECULAR:
         COPY_4V( params, CC.Light.Light[l].Specular );
         break;
      case GL_POSITION:
         COPY_4V( params, CC.Light.Light[l].Position );
         break;
      case GL_SPOT_DIRECTION:
         COPY_3V( params, CC.Light.Light[l].Direction );
         break;
      case GL_SPOT_EXPONENT:
         params[0] = CC.Light.Light[l].SpotExponent;
         break;
      case GL_SPOT_CUTOFF:
         params[0] = CC.Light.Light[l].SpotCutoff;
         break;
      case GL_CONSTANT_ATTENUATION:
         params[0] = CC.Light.Light[l].ConstantAttenuation;
         break;
      case GL_LINEAR_ATTENUATION:
         params[0] = CC.Light.Light[l].LinearAttenuation;
         break;
      case GL_QUADRATIC_ATTENUATION:
         params[0] = CC.Light.Light[l].QuadraticAttenuation;
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glGetLightfv" );
         break;
   }
}



void glGetLightiv( GLenum light, GLenum pname, GLint *params )
{
   /* TODO */
}


/********** LIGHT MODEL **********/




void gl_lightmodel( GLenum pname, const GLfloat *params )
{
   switch (pname) {
      case GL_LIGHT_MODEL_AMBIENT:
         COPY_4V( CC.Light.Model.Ambient, params );
         break;
      case GL_LIGHT_MODEL_LOCAL_VIEWER:
         if (params[0]==0.0)
            CC.Light.Model.LocalViewer = GL_FALSE;
         else
            CC.Light.Model.LocalViewer = GL_TRUE;
         break;
      case GL_LIGHT_MODEL_TWO_SIDE:
         if (params[0]==0.0)
            CC.Light.Model.TwoSide = GL_FALSE;
         else
            CC.Light.Model.TwoSide = GL_TRUE;
         CC.NewState = GL_TRUE;
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glLightModel" );
         break;
   }
}



/*
 * pname: GL_LIGHT_MODEL_LOCAL_VIEWER, GL_LIGHT_MODEL_TWO_SIDE
 */
void glLightModelf( GLenum pname, GLfloat param )
{
   gl_lightmodel( pname, &param );
}



void glLightModeli( GLenum pname, GLint param )
{
   GLfloat fparam;

   fparam = (GLfloat) param;
   gl_lightmodel( pname, &fparam );
}



/*
 * pname: GL_LIGHT_MODEL_AMBIENT red, green, blue in [1.0, -1.0]
 *        GL_LIGHT_MODEL_LOCAL_VIEWER mode where 0.0 = point, else parallel
 *        GL_LIGHT_MODEL_TWO_SIDE mode where 0.0 = one-sided, else 2-sided
 */
void glLightModelfv( GLenum pname, const GLfloat *params )
{
   gl_lightmodel( pname, params );
}



void glLightModeliv( GLenum pname, const GLint *params )
{
   GLfloat fparam[4];

   switch (pname) {
      case GL_LIGHT_MODEL_AMBIENT:
         fparam[0] = INT_TO_FLOAT( params[0] );
         fparam[1] = INT_TO_FLOAT( params[1] );
         fparam[2] = INT_TO_FLOAT( params[2] );
         fparam[3] = INT_TO_FLOAT( params[3] );
         break;
      case GL_LIGHT_MODEL_LOCAL_VIEWER:
      case GL_LIGHT_MODEL_TWO_SIDE:
         fparam[0] = (GLfloat) params[0];
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glLightModeliv" );
         return;
   }
   gl_lightmodel( pname, fparam );
}




/********** MATERIAL **********/



void gl_material( GLenum face, GLenum pname, const GLfloat *params )
{
   GLuint bitmask = 0, fmask = 0, bmask = 0;
   struct gl_material *mat;

   if (face!=GL_FRONT && face!=GL_BACK && face!=GL_FRONT_AND_BACK) {
      gl_error( GL_INVALID_ENUM, "glMaterial" );
      return;
   }

   if (face==GL_FRONT || face==GL_FRONT_AND_BACK) {
      fmask = 0xfff;
   }
   if (face==GL_BACK || face==GL_FRONT_AND_BACK) {
      bmask = 0xfff;
   }

   /* Make a bitmask indicating what material attribute(s) we're updating */
   switch (pname) {
      case GL_AMBIENT:
         bitmask |= (FRONT_AMBIENT_BIT & fmask) | (BACK_AMBIENT_BIT & bmask);
         break;
      case GL_DIFFUSE:
         bitmask |= (FRONT_DIFFUSE_BIT & fmask) | (BACK_DIFFUSE_BIT & bmask);
         break;
      case GL_SPECULAR:
         bitmask |= (FRONT_SPECULAR_BIT & fmask) | (BACK_SPECULAR_BIT & bmask);
         break;
      case GL_EMISSION:
         bitmask |= (FRONT_EMISSION_BIT & fmask) | (BACK_EMISSION_BIT & bmask);
         break;
      case GL_SHININESS:
         bitmask |= (FRONT_SHININESS_BIT & fmask) | (BACK_SHININESS_BIT & bmask);
         break;
      case GL_AMBIENT_AND_DIFFUSE:
         bitmask |= ((FRONT_AMBIENT_BIT | FRONT_DIFFUSE_BIT) & fmask)
                  | ((BACK_AMBIENT_BIT | BACK_DIFFUSE_BIT) & bmask);
         break;
      case GL_COLOR_INDEXES:
         bitmask |= (FRONT_INDEXES_BIT & fmask) | (BACK_INDEXES_BIT & bmask);
         break;
      default:
         gl_error( GL_INVALID_ENUM, "glMaterial(pname)" );
         return;
   }

   if (INSIDE_BEGIN_END) {
      /* Save per-vertex material changes in the Vertex Buffer.
       * The update_material function will eventually update the global
       * CC.Light.Material values.
       */
      mat = VB.Material[VB.Count];
      VB.MaterialMask[VB.Count] |= bitmask;
      VB.MaterialChanges = GL_TRUE;
   }
   else {
      /* just update the global material property */
      mat = CC.Light.Material;
   }

   if (bitmask & FRONT_AMBIENT_BIT) {
      COPY_4V( mat[0].Ambient, params );
   }
   if (bitmask & BACK_AMBIENT_BIT) {
      COPY_4V( mat[1].Ambient, params );
   }
   if (bitmask & FRONT_DIFFUSE_BIT) {
      COPY_4V( mat[0].Diffuse, params );
   }
   if (bitmask & BACK_DIFFUSE_BIT) {
      COPY_4V( mat[1].Diffuse, params );
   }
   if (bitmask & FRONT_SPECULAR_BIT) {
      COPY_4V( mat[0].Specular, params );
   }
   if (bitmask & BACK_SPECULAR_BIT) {
      COPY_4V( mat[1].Specular, params );
   }
   if (bitmask & FRONT_EMISSION_BIT) {
      COPY_4V( mat[0].Emission, params );
   }
   if (bitmask & BACK_EMISSION_BIT) {
      COPY_4V( mat[1].Emission, params );
   }
   if (bitmask & FRONT_SHININESS_BIT) {
      mat[0].Shininess = params[0];
   }
   if (bitmask & BACK_SHININESS_BIT) {
      mat[1].Shininess = params[0];
   }
   if (bitmask & FRONT_INDEXES_BIT) {
      mat[0].AmbientIndex = params[0];
      mat[0].DiffuseIndex = params[1];
      mat[0].SpecularIndex = params[2];
   }
   if (bitmask & BACK_INDEXES_BIT) {
      mat[1].AmbientIndex = params[0];
      mat[1].DiffuseIndex = params[1];
      mat[1].SpecularIndex = params[2];
   }
}



void glMaterialf( GLenum face, GLenum pname, GLfloat param )
{
   if (CC.CompileFlag) {
      gl_save_material( face, pname, &param );
   }
   if (CC.ExecuteFlag) {
      gl_material( face, pname, &param );
   }
}



void glMateriali( GLenum face, GLenum pname, GLint param )
{
   GLfloat fparam;

   fparam = (GLfloat) param;

   if (CC.CompileFlag) {
      gl_save_material( face, pname, &fparam );
   }
   if (CC.ExecuteFlag) {
      gl_material( face, pname, &fparam );
   }
}



void glMaterialfv( GLenum face, GLenum pname, const GLfloat *params )
{
   if (CC.CompileFlag) {
      gl_save_material( face, pname, params );
   }
   if (CC.ExecuteFlag) {
      gl_material( face, pname, params );
   }
}



void glMaterialiv( GLenum face, GLenum pname, const GLint *params )
{
   GLfloat fparam[4];

   switch (pname) {
      case GL_AMBIENT:
      case GL_DIFFUSE:
      case GL_SPECULAR:
      case GL_EMISSION:
      case GL_AMBIENT_AND_DIFFUSE:
         fparam[0] = INT_TO_FLOAT( params[0] );
         fparam[1] = INT_TO_FLOAT( params[1] );
         fparam[2] = INT_TO_FLOAT( params[2] );
         fparam[3] = INT_TO_FLOAT( params[3] );
         break;
      case GL_SHININESS:
         fparam[0] = (GLfloat) params[0];
         break;
      case GL_COLOR_INDEXES:
         fparam[0] = (GLfloat) params[0];
         fparam[1] = (GLfloat) params[1];
         fparam[2] = (GLfloat) params[2];
         break;
   }
   if (CC.CompileFlag) {
      gl_save_material( face, pname, fparam );
   }
   if (CC.ExecuteFlag) {
      gl_material( face, pname, fparam );
   }
}



void glGetMaterialfv( GLenum face, GLenum pname, GLfloat *params )
{
   GLuint f;

   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glGetMaterialfv" );
      return;
   }
   if (face==GL_FRONT) {
      f = 0;
   }
   else if (face==GL_BACK) {
      f = 1;
   }
   else {
      gl_error( GL_INVALID_ENUM, "glGetMaterialfv(face)" );
      return;
   }
   switch (pname) {
      case GL_AMBIENT:
         COPY_4V( params, CC.Light.Material[f].Ambient );
         break;
      case GL_DIFFUSE:
         COPY_4V( params, CC.Light.Material[f].Diffuse );
	 break;
      case GL_SPECULAR:
         COPY_4V( params, CC.Light.Material[f].Specular );
	 break;
      case GL_EMISSION:
	 COPY_4V( params, CC.Light.Material[f].Emission );
	 break;
      case GL_SHININESS:
	 *params = CC.Light.Material[f].Shininess;
	 break;
      case GL_COLOR_INDEXES:
	 params[0] = CC.Light.Material[f].AmbientIndex;
	 params[1] = CC.Light.Material[f].DiffuseIndex;
	 params[2] = CC.Light.Material[f].SpecularIndex;
	 break;
      default:
         gl_error( GL_INVALID_ENUM, "glGetMaterialfv(pname)" );
   }
}



void glGetMaterialiv( GLenum face, GLenum pname, GLint *params )
{
   GLuint f;

   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glGetMaterialiv" );
      return;
   }
   if (face==GL_FRONT) {
      f = 0;
   }
   else if (face==GL_BACK) {
      f = 1;
   }
   else {
      gl_error( GL_INVALID_ENUM, "glGetMaterialiv(face)" );
      return;
   }
   switch (pname) {
      case GL_AMBIENT:
         params[0] = FLOAT_TO_INT( CC.Light.Material[f].Ambient[0] );
         params[1] = FLOAT_TO_INT( CC.Light.Material[f].Ambient[1] );
         params[2] = FLOAT_TO_INT( CC.Light.Material[f].Ambient[2] );
         params[3] = FLOAT_TO_INT( CC.Light.Material[f].Ambient[3] );
         break;
      case GL_DIFFUSE:
         params[0] = FLOAT_TO_INT( CC.Light.Material[f].Diffuse[0] );
         params[1] = FLOAT_TO_INT( CC.Light.Material[f].Diffuse[1] );
         params[2] = FLOAT_TO_INT( CC.Light.Material[f].Diffuse[2] );
         params[3] = FLOAT_TO_INT( CC.Light.Material[f].Diffuse[3] );
	 break;
      case GL_SPECULAR:
         params[0] = FLOAT_TO_INT( CC.Light.Material[f].Specular[0] );
         params[1] = FLOAT_TO_INT( CC.Light.Material[f].Specular[1] );
         params[2] = FLOAT_TO_INT( CC.Light.Material[f].Specular[2] );
         params[3] = FLOAT_TO_INT( CC.Light.Material[f].Specular[3] );
	 break;
      case GL_EMISSION:
         params[0] = FLOAT_TO_INT( CC.Light.Material[f].Emission[0] );
         params[1] = FLOAT_TO_INT( CC.Light.Material[f].Emission[1] );
         params[2] = FLOAT_TO_INT( CC.Light.Material[f].Emission[2] );
         params[3] = FLOAT_TO_INT( CC.Light.Material[f].Emission[3] );
	 break;
      case GL_SHININESS:
         *params = ROUND( CC.Light.Material[f].Shininess );
	 break;
      case GL_COLOR_INDEXES:
	 params[0] = ROUND( CC.Light.Material[f].AmbientIndex );
	 params[1] = ROUND( CC.Light.Material[f].DiffuseIndex );
	 params[2] = ROUND( CC.Light.Material[f].SpecularIndex );
	 break;
      default:
         gl_error( GL_INVALID_ENUM, "glGetMaterialfv(pname)" );
   }
}




/*
 * Notes:
 *   When two-sided lighting is enabled we compute the color (or index)
 *   for both the front and back side of the primitive.  Then, when the
 *   orientation of the facet is later learned, we can determine which
 *   color (or index) to use for rendering.
 */



/* TODO:
 * Store color indexes as GLfloats instead of GLuints?
 */



/*
 * Use current lighting/material settings to compute the RGBA color of
 * a vertex.
 * Input:  vertex - vertex position in eye coordinates
 *         normal - surface normal vector
 *         twoside - 0 = front face shading only, 1 = two-sided lighting
 * Output:  frontcolor - resulting front-face color
 *          backcolor - resulting back-face color
 */
void gl_color_shade( const GLfloat vertex[4],
		     const GLfloat normal[3],
		     GLuint twoside,
                     GLfloat frontcolor[4],
		     GLfloat backcolor[4] )
{
   GLfloat R, G, B, A;
   GLfloat norm[3];
   GLuint side, i;

   for (side=0;side<=twoside;side++) {

      if (side==0) {
         /* shade frontside */
         norm[0] = normal[0];
         norm[1] = normal[1];
         norm[2] = normal[2];
      }
      else {
         /* shade backside */
         norm[0] = -normal[0];
         norm[1] = -normal[1];
         norm[2] = -normal[2];
      }

      /* Start with material emission */
      R = CC.Light.Material[side].Emission[0];
      G = CC.Light.Material[side].Emission[1];
      B = CC.Light.Material[side].Emission[2];
      A = CC.Light.Material[side].Diffuse[3];

      /* Add ambient environment */
      R += CC.Light.Model.Ambient[0] * CC.Light.Material[side].Ambient[0];
      G += CC.Light.Model.Ambient[1] * CC.Light.Material[side].Ambient[1];
      B += CC.Light.Model.Ambient[2] * CC.Light.Material[side].Ambient[2];

      /* Add contribution from each light source */
      for (i=0;i<MAX_LIGHTS;i++) {

         if (CC.Light.Light[i].Enabled) {
            GLfloat attenuation;
            GLfloat spotlight_effect;
            GLfloat ambientR, ambientG, ambientB;
            GLfloat diffuseR, diffuseG, diffuseB;
            GLfloat specularR, specularG, specularB;
            GLfloat l[3];  /* unit vector from vertex to light */
            GLfloat l_dot_norm;  /* dot product of l and norm */
            GLfloat t;

            /* compute l, d, and attenuation */
	    if (CC.Light.Light[i].Position[3]==0.0) {
	       /* directional light */
	       /* Effectively, l is a vector from the origin to the light. */
	       l[0] = CC.Light.Light[i].NormPosition[0];
	       l[1] = CC.Light.Light[i].NormPosition[1];
	       l[2] = CC.Light.Light[i].NormPosition[2];
               attenuation = 1.0F;
	    }
	    else {
	       /* positional light */
               GLfloat d;     /* distance from vertex to light */
	       l[0] = CC.Light.Light[i].Position[0] - vertex[0];
	       l[1] = CC.Light.Light[i].Position[1] - vertex[1];
	       l[2] = CC.Light.Light[i].Position[2] - vertex[2];
               d = (GLfloat) sqrt( l[0]*l[0] + l[1]*l[1] + l[2]*l[2] );
               if (d>0.001) {
                  GLfloat invd = 1.0F / d;
                  l[0] *= invd;
                  l[1] *= invd;
                  l[2] *= invd;
               }
               attenuation = 1.0F / (CC.Light.Light[i].ConstantAttenuation
                                + d * (CC.Light.Light[i].LinearAttenuation
                                + d * CC.Light.Light[i].QuadraticAttenuation));
            }

            /* ambient term */
            ambientR = CC.Light.Light[i].Ambient[0] * CC.Light.Material[side].Ambient[0];
            ambientG = CC.Light.Light[i].Ambient[1] * CC.Light.Material[side].Ambient[1];
            ambientB = CC.Light.Light[i].Ambient[2] * CC.Light.Material[side].Ambient[2];

            l_dot_norm = DOT3( l, norm );

            /* diffuse and specular terms */
            if (l_dot_norm<=0.0F) {
               /* surface faces away from light */
               diffuseR = diffuseG = diffuseB = 0.0F;
               specularR = specularG = specularB = 0.0F;
               spotlight_effect = 1.0F;
            }
            else {
               GLfloat s[3], dot, spec_coef;

               /* spotlight factor */
               if (CC.Light.Light[i].SpotCutoff==180.0F) {
                  /* not a spot light */
                  spotlight_effect = 1.0F;
               }
               else {
                  GLfloat v[3], dot;
                  v[0] = -l[0];  /* v points from light to vertex */
                  v[1] = -l[1];
                  v[2] = -l[2];
                  dot = DOT3( v, CC.Light.Light[i].Direction );
                  if (dot<=0.0F || acos(dot)*RAD2DEG > CC.Light.Light[i].SpotCutoff) {
                     /* outside of cone */
                     spotlight_effect = 0.0F;
                  }
                  else {
                     spotlight_effect = pow( dot, CC.Light.Light[i].SpotExponent );
                  }
               }

               /* diffuse term */
               diffuseR = l_dot_norm * CC.Light.Light[i].Diffuse[0]
	  		    * CC.Light.Material[side].Diffuse[0];
               diffuseG = l_dot_norm * CC.Light.Light[i].Diffuse[1]
			    * CC.Light.Material[side].Diffuse[1];
               diffuseB = l_dot_norm * CC.Light.Light[i].Diffuse[2]
			    * CC.Light.Material[side].Diffuse[2];

               /* specular term */
               if (CC.Light.Model.LocalViewer) {
		  GLfloat v[3];
		  v[0] = vertex[0];
		  v[1] = vertex[1];
		  v[2] = vertex[2];
		  NORMALIZE_3V( v );
                  s[0] = l[0] - v[0];
                  s[1] = l[1] - v[1];
                  s[2] = l[2] - v[2];
               }
               else {
                  s[0] = l[0];
                  s[1] = l[1];
                  s[2] = l[2] + 1.0F;
               }
               /* attention: s is not normalized, will be done, if necessary */
               dot = DOT3(s,norm);

	       if (dot<=0.0) {
		  spec_coef = 0.0;
	       }
	       else {
		  GLdouble p;
                  /* now `correct' the dot product */
                  dot = dot / sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2]);
		  /* must be careful of underflow here! */
		  p = pow( dot, (double) CC.Light.Material[side].Shininess );
		  if (p<1.0e-35) {
		     spec_coef = 0.0;
		  }
		  else {
		     spec_coef = (GLfloat) p;
		  }
	       }

               specularR = spec_coef * CC.Light.Light[i].Specular[0]
	 		   * CC.Light.Material[side].Specular[0];
               specularG = spec_coef * CC.Light.Light[i].Specular[1]
			   * CC.Light.Material[side].Specular[1];
               specularB = spec_coef * CC.Light.Light[i].Specular[2]
			   * CC.Light.Material[side].Specular[2];
            }

            t = attenuation * spotlight_effect;
            R += t * (ambientR + diffuseR + specularR);
            G += t * (ambientG + diffuseG + specularG);
            B += t * (ambientB + diffuseB + specularB);

         } /*if*/

      } /*for*/

      if (side==0) {
         frontcolor[0] = CLAMP( R, 0.0F, 1.0F );
         frontcolor[1] = CLAMP( G, 0.0F, 1.0F );
         frontcolor[2] = CLAMP( B, 0.0F, 1.0F );
         frontcolor[3] = CLAMP( A, 0.0F, 1.0F );
      }
      else {
         backcolor[0] = CLAMP( R, 0.0F, 1.0F );
         backcolor[1] = CLAMP( G, 0.0F, 1.0F );
         backcolor[2] = CLAMP( B, 0.0F, 1.0F );
         backcolor[3] = CLAMP( A, 0.0F, 1.0F );
      }

   } /*for side*/
}




/*
 * Use current lighting/material settings to compute the color index of
 * a vertex.
 * Input:  vertex - vertex position in viewing coordinates
 *         normal - surface normal vector
 *         twoside - 0 = front face shading only, 1 = two-sided lighting
 * Output:  frontindex - resulting front-face color index
 *          backindex - resulting back-face color index
 */
void gl_index_shade( const GLfloat vertex[4],
		     const GLfloat normal[4],
		     GLuint twoside,
		     GLfloat *frontindex,
		     GLfloat *backindex )
{
   GLfloat d_ci, s_ci;
   GLfloat diffuse, specular;  /* accumulated diffuse and specular terms */
   GLfloat d_a, s_a, index;
   GLfloat norm[3];
   GLuint side, i;

   for (side=0;side<=twoside;side++) {

      if (side==0) {
         /* shade frontside */
         norm[0] = normal[0];
         norm[1] = normal[1];
         norm[2] = normal[2];
      }
      else {
         /* shade backside */
         norm[0] = -normal[0];
         norm[1] = -normal[1];
         norm[2] = -normal[2];
      }

      diffuse = specular = 0.0;

      /* Accumulate diffuse and specular from each light source */
      for (i=0;i<MAX_LIGHTS;i++) {

         if (CC.Light.Light[i].Enabled) {
            GLfloat attenuation;
            GLfloat spotlight_effect;
            GLfloat l[3];  /* unit vector from vertex to light */
            GLfloat l_dot_norm;  /* dot product of l and norm */

            /* compute l and  attenuation */
	    if (CC.Light.Light[i].Position[3]==0.0) {
	       /* directional light */
	       /* Effectively, l is a vector from the origin to the light. */
	       l[0] = CC.Light.Light[i].NormPosition[0];
	       l[1] = CC.Light.Light[i].NormPosition[1];
	       l[2] = CC.Light.Light[i].NormPosition[2];
               attenuation = 1.0F;
	    }
	    else {
	       /* positional light */
               GLfloat d;     /* distance from vertex to light */
	       l[0] = CC.Light.Light[i].Position[0] - vertex[0];
	       l[1] = CC.Light.Light[i].Position[1] - vertex[1];
	       l[2] = CC.Light.Light[i].Position[2] - vertex[2];
               d = (GLfloat) sqrt( l[0]*l[0] + l[1]*l[1] + l[2]*l[2] );
               if (d>0.001) {
                  GLfloat invd = 1.0F / d;
                  l[0] *= invd;
                  l[1] *= invd;
                  l[2] *= invd;
               }
               attenuation = 1.0F / (CC.Light.Light[i].ConstantAttenuation
                                     + d * (CC.Light.Light[i].LinearAttenuation
                                     + d * CC.Light.Light[i].QuadraticAttenuation));
	    }

            l_dot_norm = DOT3( l, norm );

            if (l_dot_norm>0.0F) {

               /* spotlight factor */
               if (CC.Light.Light[i].SpotCutoff==180.0F) {
                  /* not a spot light */
                  spotlight_effect = 1.0F;
               }
               else {
                  GLfloat v[3], dot;
                  v[0] = -l[0];  /* v points from light to vertex */
                  v[1] = -l[1];
                  v[2] = -l[2];
                  dot = DOT3( v, CC.Light.Light[i].Direction );
                  if (dot<=0.0F || acos(dot)*RAD2DEG > CC.Light.Light[i].SpotCutoff) {
                     /* outside of cone */
		     spotlight_effect = 0.0F;
                  }
                  else {
                     spotlight_effect = pow( dot, CC.Light.Light[i].SpotExponent );
                  }
               }

               /* accumulate diffuse term */
               d_ci = 0.30F * CC.Light.Light[i].Diffuse[0]
                    + 0.59F * CC.Light.Light[i].Diffuse[1]
                    + 0.11F * CC.Light.Light[i].Diffuse[2];
               diffuse += l_dot_norm * d_ci * spotlight_effect * attenuation;

               /* accumulate specular term */
               {
                  GLfloat s[3], dot, spec_coef;

                  /* specular term */
                  if (CC.Light.Model.LocalViewer) {
		     GLfloat v[3];
		     v[0] = vertex[0];
		     v[1] = vertex[1];
		     v[2] = vertex[2];
		     NORMALIZE_3V( v );
                     s[0] = l[0] - v[0];
                     s[1] = l[1] - v[1];
                     s[2] = l[2] - v[2];
                  }
                  else {
                     s[0] = l[0];
                     s[1] = l[1];
                     s[2] = l[2] + 1.0F;
                  }
                  /* attention: s is not normalized, will be done, if necessary */
                  dot = DOT3(s,norm);

		  if (dot<=0.0F) {
		     spec_coef = 0.0;
		  }
		  else {
		     GLdouble p;
                     /* now `correct' the dot product */
                     dot = dot / sqrt(s[0]*s[0]+s[1]*s[1]+s[2]*s[2]);
		     /* must be careful of underflow here! */
		     p = pow( dot, (double)CC.Light.Material[side].Shininess );
		     if (p<1.0e-35) {
			spec_coef = 0.0;
		     }
		     else {
			spec_coef = (GLfloat) p;
		     }
		  }

                  s_ci = 0.30F * CC.Light.Light[i].Specular[0]
                       + 0.59F * CC.Light.Light[i].Specular[1]
                       + 0.11F * CC.Light.Light[i].Specular[2];
                  specular += spec_coef * s_ci * spotlight_effect * attenuation;
   	       }
            }

         } /* if */

      } /* for */

      /* Now compute color index */

      if (specular>1.0F)
         specular = 1.0F;

      if (CC.Color.DitherFlag) {
	 /* add one to index because dithering randomly subtracts one */
	 d_a = CC.Light.Material[side].DiffuseIndex
	       - CC.Light.Material[side].AmbientIndex - 1;
	 s_a = CC.Light.Material[side].SpecularIndex
   	       - CC.Light.Material[side].AmbientIndex;

	 index = CC.Light.Material[side].AmbientIndex + 1
	         + diffuse * (1.0-specular) * d_a
                 + specular * s_a;
      }
      else {
	 d_a = CC.Light.Material[side].DiffuseIndex
	       - CC.Light.Material[side].AmbientIndex;
	 s_a = CC.Light.Material[side].SpecularIndex
   	       - CC.Light.Material[side].AmbientIndex;

	 index = CC.Light.Material[side].AmbientIndex
	         + diffuse * (1.0-specular) * d_a
                 + specular * s_a;
      }

      if (index>CC.Light.Material[side].SpecularIndex) {
         index = CC.Light.Material[side].SpecularIndex;
      }

      if (side==0) {
	 *frontindex = index;
      }
      else {
         *backindex = index;
      }

   } /*for side*/
}

