/* feedback.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: feedback.c,v 1.9 1995/06/08 20:52:23 brianp Exp $

$Log: feedback.c,v $
 * Revision 1.9  1995/06/08  20:52:23  brianp
 * removed HitMin/MaxZ tests from feedback_vertex()
 *
 * Revision 1.8  1995/06/07  19:43:54  brianp
 * glRenderMode() returns -1 if feedback buffer overflows, reset counter to zero
 *
 * Revision 1.7  1995/05/22  21:02:41  brianp
 * Release 1.2
 *
 * Revision 1.6  1995/05/22  17:18:05  brianp
 * undid prev change to WRITE_RECORD due to overflow logic
 *
 * Revision 1.5  1995/05/19  13:26:50  brianp
 * added display list support for selection/name stack functions
 *
 * Revision 1.4  1995/05/12  17:00:43  brianp
 * changed CC.Mode!=0 to INSIDE_BEGIN_END
 *
 * Revision 1.3  1995/04/18  15:48:23  brianp
 * fixed assignment of NULL to function pointers to prevent warnings on Suns
 *
 * Revision 1.2  1995/03/04  19:29:44  brianp
 * 1.1 beta revision
 *
 * Revision 1.1  1995/02/24  14:22:05  brianp
 * Initial revision
 *
 */


#include "context.h"
#include "feedback.h"
#include "list.h"
#include "macros.h"




/**********************************************************************/
/*                              Feedback                              */
/**********************************************************************/


#define FB_3D		0x01
#define FB_4D		0x02
#define FB_INDEX	0x04
#define FB_COLOR	0x08
#define FB_TEXTURE	0X10



void glFeedbackBuffer( GLsizei size, GLenum type, GLfloat *buffer )
{
   if (CC.RenderMode==GL_FEEDBACK || INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glFeedbackBuffer" );
      return;
   }

   if (size<0) {
      gl_error( GL_INVALID_VALUE, "glFeedbackBuffer" );
   }

   switch (type) {
      case GL_2D:
	 CC.FeedbackMask = 0;
         CC.FeedbackType = type;
	 break;
      case GL_3D:
	 CC.FeedbackMask = FB_3D;
         CC.FeedbackType = type;
	 break;
      case GL_3D_COLOR:
	 CC.FeedbackMask = FB_3D | (CC.RGBAflag ? FB_COLOR : FB_INDEX);
         CC.FeedbackType = type;
	 break;
      case GL_3D_COLOR_TEXTURE:
	 CC.FeedbackMask = FB_3D | (CC.RGBAflag ? FB_COLOR : FB_INDEX)
	                      | FB_TEXTURE;
         CC.FeedbackType = type;
	 break;
      case GL_4D_COLOR_TEXTURE:
	 CC.FeedbackMask = FB_3D | FB_4D | (CC.RGBAflag ? FB_COLOR : FB_INDEX)
	                      | FB_TEXTURE;
         CC.FeedbackType = type;
	 break;
      default:
	 CC.FeedbackMask = 0;
         gl_error( GL_INVALID_ENUM, "glFeedbackBuffer" );
   }

   CC.FeedbackBufferSize = size;
   CC.FeedbackBuffer = buffer;
}



void gl_passthrough( GLfloat token )
{
   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glPassThrough" );
      return;
   }

   if (CC.RenderMode==GL_FEEDBACK) {
      APPEND_TOKEN( (GLfloat) GL_PASS_THROUGH_TOKEN );
      APPEND_TOKEN( token );
   }
}


void glPassThrough( GLfloat token )
{
   if (CC.CompileFlag) {
      gl_save_passthrough( token );
   }
   if (CC.ExecuteFlag) {
      gl_passthrough( token );
   }
}



/*
 * Put a vertex into the feedback buffer.
 */
void gl_feedback_vertex( GLfloat x, GLfloat y, GLfloat z, GLfloat w,
			 const GLfloat color[4], GLfloat index,
			 const GLfloat texcoord[4] )
{
   APPEND_TOKEN( x );
   APPEND_TOKEN( y );
   if (CC.FeedbackMask & FB_3D) {
      APPEND_TOKEN( z );
   }
   if (CC.FeedbackMask & FB_4D) {
      APPEND_TOKEN( w );
   }
   if (CC.FeedbackMask & FB_INDEX) {
      APPEND_TOKEN( index );
   }
   if (CC.FeedbackMask & FB_COLOR) {
      APPEND_TOKEN( color[0] );
      APPEND_TOKEN( color[1] );
      APPEND_TOKEN( color[2] );
      APPEND_TOKEN( color[3] );
   }
   if (CC.FeedbackMask & FB_TEXTURE) {
      APPEND_TOKEN( texcoord[0] );
      APPEND_TOKEN( texcoord[1] );
      APPEND_TOKEN( texcoord[2] );
      APPEND_TOKEN( texcoord[3] );
   }
}



/**********************************************************************/
/*                              Selection                             */
/**********************************************************************/


void glSelectBuffer( GLsizei size, GLuint *buffer )
{
   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glSelectBuffer" );
   }
   if (CC.RenderMode==GL_SELECT) {
      gl_error( GL_INVALID_OPERATION, "glSelectBuffer" );
   }
   CC.SelectBuffer = buffer;
   CC.SelectBufferSize = size;
   CC.SelectBufferCount = 0;

   CC.HitFlag = GL_FALSE;
   CC.HitMinZ = 1.0;
   CC.HitMaxZ = 0.0;
}


void glInitNames( void )
{
   if (CC.CompileFlag) {
      gl_save_initnames();
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glInitNames" );
      }
      CC.NameStackDepth = 0;
      CC.HitFlag = GL_FALSE;
      CC.HitMinZ = 1.0;
      CC.HitMaxZ = 0.0;
   }
}



#define WRITE_RECORD( V )					\
	if (CC.SelectBufferCount < CC.SelectBufferSize) {	\
	   CC.SelectBuffer[CC.SelectBufferCount] = (V);		\
	}							\
	CC.SelectBufferCount++;



static void write_hit_record( void )
{
   if (CC.SelectBufferCount < CC.SelectBufferSize) {
      GLuint i;
      GLuint zmin, zmax;

      /* HitMinZ and HitMaxZ are in [0,1].  Multiply these values by */
      /* 2^32-1 and round to nearest unsigned integer. */

      zmin = (GLuint) ((GLfloat) 0xffffffff * CC.HitMinZ);
      zmax = (GLuint) ((GLfloat) 0xffffffff * CC.HitMaxZ);

      WRITE_RECORD( CC.NameStackDepth );
      WRITE_RECORD( zmin );
      WRITE_RECORD( zmax );
      for (i=0;i<CC.NameStackDepth;i++) {
         WRITE_RECORD( CC.NameStack[i] );
      }

      CC.SelectHits++;
   }

   /* TODO: correct??? */
   CC.HitFlag = GL_FALSE;
   CC.HitMinZ = 1.0;
   CC.HitMaxZ = -1.0;
}



void glLoadName( GLuint name )
{
   if (CC.CompileFlag) {
      gl_save_loadname( name );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glLoadName" );
	 return;
      }
      if (CC.RenderMode!=GL_SELECT) {
	 return;
      }
      if (CC.NameStackDepth==0) {
	 gl_error( GL_INVALID_OPERATION, "glLoadName" );
	 return;
      }
      if (CC.HitFlag) {
	 write_hit_record();
      }
      if (CC.NameStackDepth<MAX_NAME_STACK_DEPTH) {
	 CC.NameStack[CC.NameStackDepth-1] = name;
      }
      else {
	 CC.NameStack[MAX_NAME_STACK_DEPTH-1] = name;
      }
   }
}


void glPushName( GLuint name )
{
   if (CC.CompileFlag) {
      gl_save_pushname( name );
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glPushName" );
	 return;
      }
      if (CC.RenderMode!=GL_SELECT) {
	 return;
      }
      if (CC.HitFlag) {
	 write_hit_record();
      }
      if (CC.NameStackDepth<MAX_NAME_STACK_DEPTH) {
	 CC.NameStack[CC.NameStackDepth++] = name;
      }
      else {
	 gl_error( GL_STACK_OVERFLOW, "glPushName" );
      }
   }
}


void glPopName( void )
{
   if (CC.CompileFlag) {
      gl_save_popname();
   }
   if (CC.ExecuteFlag) {
      if (INSIDE_BEGIN_END) {
	 gl_error( GL_INVALID_OPERATION, "glPopName" );
	 return;
      }
      if (CC.RenderMode!=GL_SELECT) {
	 return;
      }
      if (CC.HitFlag) {
	 write_hit_record();
      }
      if (CC.NameStackDepth>0) {
	 CC.NameStackDepth--;
      }
      else {
	 gl_error( GL_STACK_UNDERFLOW, "glPopName" );
      }
   }
}



/**********************************************************************/
/*                           Render Mode                              */
/**********************************************************************/



GLint glRenderMode( GLenum mode )
{
   GLint result;

   if (INSIDE_BEGIN_END) {
      gl_error( GL_INVALID_OPERATION, "glInitNames" );
   }

   switch (CC.RenderMode) {
      case GL_RENDER:
	 result = 0;
	 break;
      case GL_SELECT:
	 if (CC.HitFlag) {
	    write_hit_record();
	 }
	 if (CC.SelectBufferCount > CC.SelectBufferSize) {
	    /* overflow */
	    result = -1;
	 }
	 else {
	    result = CC.SelectHits;
	 }
	 CC.SelectBufferCount = 0;
	 CC.SelectHits = 0;
	 CC.NameStackDepth = 0;
	 break;
      case GL_FEEDBACK:
	 if (CC.FeedbackCount > CC.FeedbackBufferSize) {
	    /* overflow */
	    result = -1;
	 }
	 else {
	    result = CC.FeedbackCount;
	 }
	 CC.FeedbackCount = 0;
	 break;
   }

   switch (mode) {
      case GL_RENDER:
         break;
      case GL_SELECT:
	 if (CC.SelectBufferSize==0) {
	    /* haven't called glSelectBuffer yet */
	    gl_error( GL_INVALID_OPERATION, "glRenderMode" );
	 }
	 break;
      case GL_FEEDBACK:
	 if (CC.FeedbackBufferSize==0) {
	    /* haven't called glFeedbackBuffer yet */
	    gl_error( GL_INVALID_OPERATION, "glRenderMode" );
	 }
	 break;
      default:
	 gl_error( GL_INVALID_ENUM, "glRenderMode" );
	 return 0;
   }

   CC.RenderMode = mode;

   CC.LineFunc = ( void (*)() ) 0;
   CC.PointFunc = ( void (*)() ) 0;
   CC.PolygonFunc = ( void (*)() ) 0;

   return result;
}

