/* $Id: vbxform.c,v 1.23 2000/03/21 01:27:46 keithw Exp $ */

/*
 * Mesa 3-D graphics library
 * Version:  3.1
 * 
 * Copyright (C) 1999  Brian Paul   All Rights Reserved.
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a
 * copy of this software and associated documentation files (the "Software"),
 * to deal in the Software without restriction, including without limitation
 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
 * and/or sell copies of the Software, and to permit persons to whom the
 * Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included
 * in all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
 * BRIAN PAUL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
 * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 */



#ifdef PC_HEADER
#include "all.h"
#else
#include "glheader.h"
#include "context.h"
#include "cva.h"
#include "clip.h"
#include "eval.h"
#include "enums.h"
#include "dlist.h"
#include "fog.h"
#include "light.h"
#include "macros.h"
#include "matrix.h"
#include "mem.h"
#include "mmath.h"
#include "pipeline.h"
#include "shade.h"
#include "state.h"
#include "texture.h"
#include "types.h"
#include "varray.h"
#include "vb.h"
#include "vbcull.h"
#include "vbfill.h"
#include "vbrender.h"
#include "vbxform.h"
#include "xform.h"
#endif


void gl_maybe_transform_vb( struct immediate *IM )
{
   GLcontext *ctx = IM->backref;

   if (ctx->NewState)
      gl_update_state(ctx);

   if (IM->FlushElt) {
      gl_exec_array_elements( ctx, IM, IM->LastPrimitive, IM->Count );
      IM->FlushElt = 0;
   }

   gl_compute_orflag( IM );
 
   if (ctx->ExecuteFlag) 
      gl_cva_compile_cassette( ctx, IM );
   else
      gl_fixup_input( ctx, IM );
   
   if (ctx->CompileFlag) 
      gl_compile_cassette( ctx );
   else
      gl_reset_input( ctx );
}


void gl_flush_vb( GLcontext *ctx, const char *where )
{
   struct immediate *IM = ctx->input;

   if (MESA_VERBOSE&VERBOSE_PIPELINE)
      fprintf(stderr, "gl_flush_vb: %s\n", where);

   gl_maybe_transform_vb( IM );
}

/* Drivers can call this inside their swapbuffers routines:
 */
void gl_internal_flush( GLcontext *ctx )
{
   FLUSH_VB( ctx, "internal flush" );
}


#define RESET_VEC(v, t, s, c) (v.start = t v.data[s], v.count = c)  

/* 
 *
 */
void gl_reset_vb( struct vertex_buffer *VB )
{
   GLuint copy;
   GLuint dst;
   GLuint start;
   struct immediate *IM = VB->IM;
   GLubyte clipor = VB->ClipOrMask;

   if (!VB->CullDone)
      gl_fast_copy_vb( VB );
      
   copy = VB->CopyCount;
   start = 3-copy;

   VB->CopyStart = start;
   VB->ClipOrMask = 0;
   VB->ClipAndMask = CLIP_ALL_BITS;

   if (IM) 
   {
      if (VB->Count != IM->Count && 0) {
	 fprintf(stderr, "Trying to copy vertices in the middle of an IM (%d/%d)!!\n",
		 VB->Count, IM->Count);
      }
      else if (VB->pipeline->copy_transformed_data) {

	 for (dst = start ; dst < VB_START ; dst++) 
	 {
	    GLuint src = VB->Copy[dst];
	    
	    if (MESA_VERBOSE&VERBOSE_IMMEDIATE)
	       fprintf(stderr, "copying vertex %u to %u\n", src, dst);
	    
	    COPY_4FV( VB->Clip.data[dst], VEC_ELT(VB->ClipPtr, GLfloat, src) ); 
	    COPY_4FV( VB->Win.data[dst], VB->Win.data[src] ); 
	    
	    VB->UserClipMask[dst] = VB->UserClipMask[src];
	    VB->ClipMask[dst] = (GLubyte) (VB->ClipMask[src] & ~CLIP_CULLED_BIT);
	    VB->ClipAndMask &= VB->ClipMask[dst];
	    VB->ClipOrMask  |= VB->ClipMask[dst];
	    VB->ClipMask[src] = 0;	/* hack for bounds_cull_vb */

	    COPY_4UBV( IM->Color[dst], IM->Color[src] );
	    COPY_4UBV( VB->Spec[0][dst], VB->Spec[0][src] );
	    COPY_4UBV( VB->Spec[1][dst], VB->Spec[1][src] );
	    COPY_4UBV( VB->BColor.data[dst], VB->BColor.data[src] );
	    IM->Index[dst] = IM->Index[src];
	    VB->BIndex.data[dst] = VB->BIndex.data[src];

	    if (VB->TexCoordPtr[0] == &IM->v.TexCoord[0])
	       COPY_4FV( IM->TexCoord[0][dst], IM->TexCoord[0][src] );
	    
	    if (VB->TexCoordPtr[1] == &IM->v.TexCoord[1])
	       COPY_4FV( IM->TexCoord[1][dst], IM->TexCoord[1][src] );

	    IM->Elt[dst] = IM->Elt[src];
	    VB->SavedOrFlag |= IM->Flag[src];
	 } 
      }
      
      VB->CullDone = 0;
   }

   if (clipor & CLIP_USER_BIT) 
      MEMSET(VB->UserClipMask + VB->Start, 0, VB->Count);

   VB->NormCullStart = 0;
   VB->Parity = (VB->LastPrimitive^VB->Count)&1;
   VB->PurgeFlags = 0;
   VB->EarlyCull = 1;
   VB->Culled = 0;
   VB->BoundsPtr = 0;
   VB->NormalLengthPtr = 0;  
   VB->Indirect = 0;
   VB->Culled = 0;
}




/* Copy the untransformed parts of the overlapping vertices from one
 * immediate struct to another (or possibly the same one).  
 *
 * Only copy those elements which are genuinely untransformed.  Others
 * are done in gl_reset_vb.
 */
void gl_copy_prev_vertices( struct vertex_buffer *VB,
			    struct immediate *prev,
			    struct immediate *next )
{
   GLuint dst;
   GLuint flags = VB->pipeline->inputs;

   if (MESA_VERBOSE&VERBOSE_CULL)
      fprintf(stderr, "copy prev vertices im: prev %d next %d copystart %d\n", 
	      prev->id, next->id, VB->CopyStart);

   /* VB_START is correct as vertex copying is only required when an
    * IM wraps, which means that the next VB must start at VB_START.
    */
   for (dst = VB->CopyStart ; dst < VB_START ; dst++) 
   {
      GLuint src = VB->Copy[dst];

      if (MESA_VERBOSE&VERBOSE_CULL)
	 fprintf(stderr, "copy_prev: copy %d to %d\n", src, dst );

      COPY_4FV( next->Obj[dst], prev->Obj[src] );

      if ((flags&VERT_TEX0_ANY) && VB->TexCoordPtr[0] == &prev->v.TexCoord[0])
	 COPY_4FV( next->TexCoord[0][dst], prev->TexCoord[0][src] );
	    
      if ((flags&VERT_TEX1_ANY) && VB->TexCoordPtr[1] == &prev->v.TexCoord[1])
	 COPY_4FV( next->TexCoord[1][dst], prev->TexCoord[1][src] );

      COPY_4UBV( next->Color[dst], prev->Color[src] );
      next->Index[dst] = prev->Index[src];
      next->EdgeFlag[dst] = prev->EdgeFlag[src];

      next->Elt[dst] = prev->Elt[src];
      VB->SavedOrFlag |= prev->Flag[src];
   }
}



void RESET_IMMEDIATE( GLcontext *ctx ) 
{
   if (ctx->VB->prev_buffer != ctx->VB->IM) {
      /* Should only get here if we are trying to use the internal
       * interfaces, eg gl_Vertex3f(), gl_Begin() from inside a
       * display list.  In this case, it is necessary to pull the
       * current values into the ctx->VB.store buffer, because this
       * may not have been done.
       */
      FLUSH_VB( ctx, "RESET_IMMEDIATE" );
      gl_reset_input( ctx );
   }
}


/* Called to initialize new buffers, and to recycle old ones.
 */
void gl_reset_input( GLcontext *ctx )
{
   struct immediate *IM = ctx->input;

   MEMSET(IM->Flag, 0, sizeof(GLuint) * (IM->Count+2));
   IM->Start = VB_START;
   IM->Count = VB_START;
   
   IM->Primitive[IM->Start] = ctx->Current.Primitive;
   IM->LastPrimitive = IM->Start;
   IM->BeginState = VERT_BEGIN_0;         
   IM->OrFlag = 0;
   IM->AndFlag = ~0U;

   if (0)
      fprintf(stderr, 
	   "in reset_input(IM %d), BeginState is %x, setting prim[%d] to %s\n", 
	   IM->id,
	   VERT_BEGIN_0,
	   IM->Start, gl_lookup_enum_by_nr(ctx->Current.Primitive));

   IM->ArrayAndFlags = ~ctx->Array.Flags;
   IM->ArrayIncr = ctx->Array.Vertex.Enabled;
   IM->ArrayEltFlush = !(ctx->CompileCVAFlag);
}


/* Preserves size information as well.
 */
static void 
fixup_4f( GLfloat data[][4], GLuint flag[], GLuint start, GLuint match )
{
   GLuint i = start;

   for (;;) {
      if ((flag[++i] & match) == 0) {
	 COPY_4FV(data[i], data[i-1]);
	 flag[i] |= (flag[i-1] & match);
	 if (flag[i] & VERT_END_VB) break;
      } 
   }
}



/* Only needed for buffers with eval coords.
 */
static void
fixup_3f( float data[][3], GLuint flag[], GLuint start, GLuint match )
{
   GLuint i = start;

   for (;;) {
      if ((flag[++i] & match) == 0) {
	 COPY_3V(data[i], data[i-1]);
	 flag[i] |= match;
	 if (flag[i] & VERT_END_VB) break;
      } 
   }
}


static void
fixup_1ui( GLuint *data, GLuint flag[], GLuint start, GLuint match )
{
   GLuint i = start;

   for (;;) {
      if ((flag[++i] & match) == 0) {
	 data[i] = data[i-1];
	 if (flag[i] & VERT_END_VB) break;
      } 
   }
   flag[i] |= match;
}

static void
fixup_1ub( GLubyte *data, GLuint flag[], GLuint start, GLuint match )
{
   GLuint i = start;

   for (;;) {
      if ((flag[++i] & match) == 0) {
	 data[i] = data[i-1];
	 if (flag[i] & VERT_END_VB) break;
      } 
   }
   flag[i] |= match;
}


static void
fixup_4ub( GLubyte data[][4], GLuint flag[], GLuint start, GLuint match )
{
   GLuint i = start;

   for (;;) {
      if ((flag[++i] & match) == 0) {
	 COPY_4UBV(data[i], data[i-1]);
	 if (flag[i] & VERT_END_VB) break;
      } 
   }
   flag[i] |= match;
}


/* Do this instead of fixup for shared normals.
 */
static void
find_last_3f( float data[][3], GLuint flag[], GLuint match, GLuint count )
{
   int i = count;

   do {
      if ((flag[--i] & match) != 0) {
	 COPY_3V(data[count], data[i]);	 
	 return;      
      }
   } while (i >= 0);

   /* To reach this point excercises a bug that seems only to exist on
    * dec alpha installations.  I want to leave this print statement
    * enabled on the 3.3 branch so that we are reminded to track down
    * the problem.
    */
   fprintf(stderr, 
	   "didn't find VERT_NORM in find_last_3f"
	   "(Dec alpha problem?)\n");
}

static void
fixup_first_4v( GLfloat data[][4], GLuint flag[], GLuint match, 
		GLuint start, GLfloat *dflt )
{
   GLuint i = start-1;
   match |= VERT_END_VB;

   while ((flag[++i]&match) == 0)
      COPY_4FV(data[i], dflt);
}


static void
fixup_first_1ui( GLuint data[], GLuint flag[], GLuint match,
		 GLuint start, GLuint dflt )
{
   GLuint i = start-1;
   match |= VERT_END_VB;

   while ((flag[++i]&match) == 0)
      data[i] = dflt;
}


static void
fixup_first_1ub( GLubyte data[], GLuint flag[], GLuint match,
		 GLuint start, GLubyte dflt )
{
   GLuint i = start-1;
   match |= VERT_END_VB;

   while ((flag[++i]&match) == 0)
      data[i] = dflt;
}


static void
fixup_first_4ub( GLubyte data[][4], GLuint flag[], GLuint match,
		 GLuint start, GLubyte dflt[4] )
{
   GLuint i = start-1;
   match |= VERT_END_VB;

   while ((flag[++i]&match) == 0)
      COPY_4UBV(data[i], dflt);
}




static GLuint vertex_sizes[16] = { 0, 
				   1, 
				   2, 2, 
				   3, 3, 3, 3, 
				   4, 4, 4, 4, 4, 4, 4, 4 };



GLuint gl_texcoord_size( GLuint flag, GLuint unit )
{
   flag >>= VERT_TEX0_SHIFT + unit * NR_TEXSIZE_BITS;
   return vertex_sizes[flag & 0xf];
}


static void set_vec_sizes( struct immediate *IM, GLuint orflag )
{
   GLuint i;

   if (orflag & VERT_OBJ_ANY) {
      GLuint szflag = orflag & VERT_OBJ_234;
      IM->v.Obj.size = vertex_sizes[szflag<<1];
   }

   for (i = 0 ; i < MAX_TEXTURE_UNITS ; i++) {
      if (orflag & VERT_TEX_ANY(i)) {
	 GLuint szflag = ((orflag>>(VERT_TEX0_SHIFT+i*NR_TEXSIZE_BITS))
			  & 0xf);
	 IM->v.TexCoord[i].size = vertex_sizes[szflag];
      }
   }
}


void gl_compute_orflag( struct immediate *IM )
{
   GLuint count = IM->Count;
   GLuint orflag = 0;
   GLuint andflag = ~0U;
   GLuint i;

   IM->LastData = count-1;

/*     fprintf(stderr, "In gl_compute_orflag, start %d count %d\n", IM->Start, IM->Count); */
/*     gl_print_vert_flags("initial andflag", andflag); */

   /* Compute the flags for the whole buffer, even if 
    */
   for (i = IM->Start ; i < count ; i++) {
      andflag &= IM->Flag[i];
      orflag |= IM->Flag[i];
   }

   if (IM->Flag[i] & VERT_DATA) {
      IM->LastData++;
/*        andflag &= IM->Flag[i]; */
      orflag |= IM->Flag[i];
   }      

/*     gl_print_vert_flags("final andflag", andflag); */

   IM->Flag[IM->LastData+1] |= VERT_END_VB;
   IM->AndFlag = andflag;
   IM->OrFlag = orflag;
}

void gl_fixup_input( GLcontext *ctx, struct immediate *IM )
{
   GLuint count = IM->Count;
   GLuint start = IM->Start;
   GLuint fixup, diff;
   GLuint andflag = IM->AndFlag;
   GLuint orflag = IM->OrFlag;

   IM->Primitive[count] = IM->Primitive[IM->LastPrimitive];
   IM->NextPrimitive[IM->LastPrimitive] = count;
   IM->NextPrimitive[count] = count+1;

   if (MESA_VERBOSE&VERBOSE_IMMEDIATE) 
   {
      fprintf(stderr, "Start: %u Count: %u LastData: %u\n", 
	      IM->Start, IM->Count, IM->LastData);
      gl_print_vert_flags("Orflag", orflag);
      gl_print_vert_flags("Andflag", andflag);
   }

   /* Array elements modify the current state - must do this before
    * fixup.
    */
   if (ctx->CompileCVAFlag && !(andflag & VERT_ELT)) 
      gl_rescue_cva( ctx, IM );

   if (orflag & VERT_ELT) 
   {
      orflag = IM->OrFlag;
      andflag = IM->AndFlag;
      start = IM->Start;
   }
   
   fixup = ~andflag & VERT_FIXUP;

   if (!ctx->CompileFlag) 
      fixup &= ctx->CVA.elt.inputs; 
   
   if (!ctx->ExecuteFlag)
      fixup &= orflag;

   if (ctx->CompileCVAFlag)
      fixup &= ~ctx->CVA.pre.outputs;

   if ((orflag & (VERT_OBJ_ANY|VERT_EVAL_ANY)) == 0)
      fixup = 0;

   if (fixup) 
   {

      if (ctx->ExecuteFlag && (fixup & ~IM->Flag[start])) {
	 GLuint copy = fixup & ~IM->Flag[start];

	 if (copy & VERT_NORM) 	 
	    COPY_3V( IM->Normal[start], ctx->Current.Normal );
	 
	 if (copy & VERT_RGBA)
	    COPY_4UBV( IM->Color[start], ctx->Current.ByteColor);
      
	 if (copy & VERT_INDEX)
	    IM->Index[start] = ctx->Current.Index;
	 
	 if (copy & VERT_EDGE)
	    IM->EdgeFlag[start] = ctx->Current.EdgeFlag;

	 if (copy & VERT_TEX0_ANY)
	    COPY_4FV( IM->TexCoord[0][start], ctx->Current.Texcoord[0] ); 
	 
	 if (copy & VERT_TEX1_ANY)
	    COPY_4FV( IM->TexCoord[1][start], ctx->Current.Texcoord[1] ); 
      }


      if (MESA_VERBOSE&VERBOSE_IMMEDIATE)
	 gl_print_vert_flags("fixup", fixup);
      
      if (fixup & VERT_TEX0_ANY) {
	 if (orflag & VERT_TEX0_ANY)
	    fixup_4f( IM->TexCoord[0], IM->Flag, start, VERT_TEX0_1234 );
	 else 
	    fixup_first_4v( IM->TexCoord[0], IM->Flag, 0, start, 
			    IM->TexCoord[0][start]);
      }

      if (fixup & VERT_TEX1_ANY) {
	 if (orflag & VERT_TEX1_ANY)
	    fixup_4f( IM->TexCoord[1], IM->Flag, start, VERT_TEX1_1234 );
	 else 
	    fixup_first_4v( IM->TexCoord[1], IM->Flag, 0, start, 
			    IM->TexCoord[1][start] );
      }

      if (fixup & VERT_EDGE) {
	 if (orflag & VERT_EDGE)
	    fixup_1ub( IM->EdgeFlag, IM->Flag, start, VERT_EDGE );
	 else 
	    fixup_first_1ub( IM->EdgeFlag, IM->Flag, 0, start, 
			     IM->EdgeFlag[start] );
      }

      if (fixup & VERT_INDEX) {
	 if (orflag & VERT_INDEX)
	    fixup_1ui( IM->Index, IM->Flag, start, VERT_INDEX );
	 else 
	    fixup_first_1ui( IM->Index, IM->Flag, 0, start, IM->Index[start] );
      }

      if (fixup & VERT_RGBA) {
	 if (orflag & VERT_RGBA) 
	    fixup_4ub( IM->Color, IM->Flag, start, VERT_RGBA );      
	 else 
	    fixup_first_4ub( IM->Color, IM->Flag, 0, start, IM->Color[start] );
      }

      if (fixup & VERT_NORM) {
	 /* Only eval cannot use the Flag member to find valid normals:
	  */
	 if (IM->OrFlag & VERT_EVAL_ANY)
	    fixup_3f( IM->Normal, IM->Flag, start, VERT_NORM );
	 else {
	    /* Copy-to-current requires a valid normal in the last slot:
	     */
	    if ((IM->OrFlag & VERT_NORM) &&
		!(IM->Flag[IM->LastData] & VERT_NORM)) 
	       find_last_3f( IM->Normal, IM->Flag, VERT_NORM, IM->LastData );
	 }
      }
   }

   diff = count - start;
   IM->v.Obj.count = diff;
   IM->v.Normal.count = diff;
   IM->v.TexCoord[0].count = diff;
   IM->v.TexCoord[1].count = diff;
   IM->v.EdgeFlag.count = diff;
   IM->v.Color.count = diff;
   IM->v.Index.count = diff;
 
   /* Prune possible half-filled slot.
    */
   IM->Flag[IM->LastData+1] &= ~VERT_END_VB;
   IM->Flag[IM->Count] |= VERT_END_VB;
}


static void calc_normal_lengths( GLfloat *dest, 
				 CONST GLfloat (*data)[3],
				 const GLuint *flags,
				 GLuint count )
{
   GLuint i;

   for (i = 0 ; i < count ; i++ )
      if (flags[i] & VERT_NORM) {
	 GLfloat tmp = (GLfloat) LEN_3FV( data[i] );
	 dest[i] = 0;
	 if (tmp > 0)
	    dest[i] = 1.0F / tmp;
      }      
}


/* Revive a compiled immediate struct - propogate new 'Current'
 * values.  Often this is redundant because the current values were
 * known and fixed up at compile time.
 */
void gl_fixup_cassette( GLcontext *ctx, struct immediate *IM )
{
   GLuint fixup;
   GLuint count = IM->Count;
   GLuint start = IM->Start;
   
   if (count == start) 
      return;

   if (ctx->NewState)
      gl_update_state( ctx );


   if (ctx->Transform.Normalize && IM->LastCalcedLength < IM->Count) {
      GLuint start = IM->LastCalcedLength;

      if (!IM->NormalLengths)
	 IM->NormalLengths = (GLfloat *)MALLOC(sizeof(GLfloat) * VB_SIZE);

      calc_normal_lengths( IM->NormalLengths + start, 
			   (const GLfloat (*)[3])(IM->Normal + start), 
			   IM->Flag + start, 
			   IM->Count - start);
      
      IM->LastCalcedLength = IM->Count;
   }

   fixup = ctx->CVA.elt.inputs & ~IM->AndFlag & VERT_FIXUP;

   if (fixup) {

      if (MESA_VERBOSE & VERBOSE_IMMEDIATE) {
	 fprintf(stderr, "start: %d count: %d\n", start, count);
	 gl_print_vert_flags("fixup_cassette", fixup);
      }

      if (fixup & VERT_TEX0_ANY) 
	 fixup_first_4v( IM->TexCoord[0], IM->Flag, VERT_TEX0_ANY, start,
			 ctx->Current.Texcoord[0] );

      if (fixup & VERT_TEX1_ANY) 
	 fixup_first_4v( IM->TexCoord[1], IM->Flag, VERT_TEX1_ANY, start,
			 ctx->Current.Texcoord[1] );

      if (fixup & VERT_EDGE)
	 fixup_first_1ub(IM->EdgeFlag, IM->Flag, VERT_EDGE, start,
			 ctx->Current.EdgeFlag );

      if (fixup & VERT_INDEX)
	 fixup_first_1ui(IM->Index, IM->Flag, VERT_INDEX, start,
			 ctx->Current.Index );
   
      if (fixup & VERT_RGBA) 
	 fixup_first_4ub(IM->Color, IM->Flag, VERT_RGBA, start,
			 ctx->Current.ByteColor );      

      if ((fixup & VERT_NORM) && !(IM->Flag[start] & VERT_NORM)) {
	 COPY_3V(IM->Normal[start], ctx->Current.Normal);
	 if (ctx->Transform.Normalize)
	    IM->NormalLengths[start] = 1.0F / (GLfloat) LEN_3FV(ctx->Current.Normal);
      }
   }  
}



static void fixup_primitives( struct vertex_buffer *VB, struct immediate *IM )
{
   static GLuint increment[GL_POLYGON+2] = { 1,2,1,1,3,1,1,4,2,1,1 };
   static GLuint intro[GL_POLYGON+2]     = { 0,0,2,2,0,2,2,0,2,2,0 };
   GLcontext *ctx = VB->ctx;
   const GLuint *flags = IM->Flag;
   const GLuint *in_prim = IM->Primitive;
   const GLuint *in_nextprim = IM->NextPrimitive;
   GLuint *out_prim = VB->IM->Primitive;
   GLuint *out_nextprim = VB->IM->NextPrimitive;
   GLuint count = VB->Count;
   GLuint start = VB->Start;
   GLuint in, out, last;
   GLuint incr, prim;   
   GLuint transition;
   GLuint interesting;
   GLuint err;
   
/*     printf("IM: %d VB->Count %d VB->Start %d current prim: %d\n", IM->id, VB->Count, VB->Start, ctx->Current.Primitive);  */

   if (ctx->Current.Primitive == GL_POLYGON+1) {
      transition = VERT_BEGIN;
      err = IM->BeginState & VERT_ERROR_1;
   } else {
      transition = VERT_END;
      err = IM->BeginState & VERT_ERROR_0;
   }

   if (err) {
      /* Occurred somewhere inside the vb.  Don't know/care where/why.
       */
      gl_error( ctx, GL_INVALID_OPERATION, "begin/end");
   }

   interesting = transition | VERT_END_VB;


   for (in = start ; in <= count ; in = in_nextprim[in]) 
      if (flags[in] & interesting) 
	 break;

   out = VB->CopyStart;

   if (in == out) {
      out_nextprim[out] = in_nextprim[in];
      out_prim[out] = in_prim[in];
      last = IM->LastPrimitive;
   } else if (flags[in] & transition) {
      out_nextprim[out] = in;
      out_prim[out] = ctx->Current.Primitive;
      out = in;
      last = IM->LastPrimitive;
   } else {
      out_nextprim[out] = in;
      out_prim[out] = ctx->Current.Primitive;
      in++;
      last = out;
   }

   for ( ; in <= count ; in = in_nextprim[in] ) {
      out_prim[in] = in_prim[in];
      out_nextprim[in] = in_nextprim[in];
   }
	  

   VB->Primitive = out_prim;
   VB->NextPrimitive = out_nextprim;
   VB->LastPrimitive = last;
   prim = ctx->Current.Primitive = (GLenum) out_prim[last];


   /* Calculate whether the primitive finished on a 'good number' of
    * vertices, or whether there are overflowing vertices we have to
    * copy to the next buffer (in addition to the normal ones required
    * by a continuing primitive).
    *
    * Note that GL_POLYGON+1, ie outside begin/end, has increment 1.
    */

   incr = increment[prim];
  
   if (incr != 1 && (count - last - intro[prim])) 
      VB->Ovf = (count - last - intro[prim]) % incr; 
   else
      VB->Ovf = 0;

   if (0) 
      fprintf(stderr, "prim: %s count %u last %u incr %u ovf: %u\n",
	      gl_prim_name[prim], count, last, incr, VB->Ovf);
}


void gl_copy_to_current( GLcontext *ctx, struct immediate *IM )
{
   GLuint count = IM->LastData;
   GLuint flag = IM->OrFlag;
   GLuint mask = 0;

   if (MESA_VERBOSE&VERBOSE_IMMEDIATE)
      gl_print_vert_flags("copy to current", flag);

   if (flag & VERT_NORM) 
      COPY_3FV( ctx->Current.Normal, IM->Normal[count]);   
   
   if (flag & VERT_INDEX)
      ctx->Current.Index = IM->Index[count];

   if (flag & VERT_EDGE)
      ctx->Current.EdgeFlag = IM->EdgeFlag[count];

   if (flag & VERT_RGBA) 
      COPY_4UBV(ctx->Current.ByteColor, IM->Color[count]);

   if (flag & VERT_TEX0_ANY) {
      mask |= VERT_TEX0_1234;
      COPY_4FV( ctx->Current.Texcoord[0], IM->TexCoord[0][count]);
   }
      
   if (flag & VERT_TEX1_ANY) {
      mask |= VERT_TEX1_1234;
      COPY_4FV( ctx->Current.Texcoord[1], IM->TexCoord[1][count]);
   }

   /* Save the texcoord size information as well.
    */
   ctx->Current.Flag &= ~mask;
   ctx->Current.Flag |= IM->Flag[count] & mask;
}


void gl_execute_cassette( GLcontext *ctx, struct immediate *IM )
{
   struct vertex_buffer *VB = ctx->VB;
   struct immediate *prev = VB->prev_buffer;
   GLuint vec_start, diff;

   IM->ref_count++;

   if (prev != IM || IM != VB->IM) {
      gl_copy_prev_vertices( VB, VB->prev_buffer, IM );
   }

   if (! --prev->ref_count ) 
      gl_immediate_free( prev );

   VB->prev_buffer = IM;
   VB->Start = IM->Start;
   VB->Count = IM->Count;
   VB->Flag = IM->Flag;
   VB->OrFlag = IM->OrFlag | VB->SavedOrFlag;
   VB->EltPtr = &IM->v.Elt;
   VB->MaterialMask = IM->MaterialMask;
   VB->Material = IM->Material;
   VB->CullMode = (GLubyte) ((IM->AndFlag & VERT_NORM) ? 0 : COMPACTED_NORMALS);
   VB->ObjPtr = &IM->v.Obj;
   VB->NormalPtr = &IM->v.Normal;
   VB->ColorPtr = &IM->v.Color;
   VB->Color[0] = VB->Color[1] = VB->ColorPtr;
   VB->IndexPtr = &IM->v.Index;
   VB->EdgeFlagPtr = &IM->v.EdgeFlag;
   VB->TexCoordPtr[0] = &IM->v.TexCoord[0];
   VB->TexCoordPtr[1] = &IM->v.TexCoord[1];
/*     VB->BoundsPtr = IM->Bounds; */
   VB->NormalLengthPtr = IM->NormalLengths;
   VB->IndirectCount = VB->Count;
   VB->SavedOrFlag = 0;

   if (IM->Start != VB_START)
      VB->CopyStart = IM->Start;

   vec_start = IM->Start;
   if (vec_start == VB_START && VB->pipeline->replay_copied_vertices)
      vec_start = VB->CopyStart;

   if (MESA_VERBOSE&VERBOSE_IMMEDIATE)
      fprintf(stderr, "reseting vectors to %d/%d .. %d\n", vec_start, IM->Start, IM->Count);

   VB->LastPrimitive = IM->Start; 

   diff = IM->Count - vec_start;

   RESET_VEC(IM->v.Obj, (GLfloat *), vec_start, diff);
   RESET_VEC(IM->v.Normal, (GLfloat *), vec_start, diff);
   RESET_VEC(IM->v.TexCoord[0], (GLfloat *), vec_start, diff);
   RESET_VEC(IM->v.TexCoord[1], (GLfloat *), vec_start, diff);
   RESET_VEC(IM->v.Index, &, vec_start, diff);
   RESET_VEC(IM->v.Elt, &, vec_start, diff);
   RESET_VEC(IM->v.EdgeFlag, &, vec_start, diff);
   RESET_VEC(IM->v.Color, (GLubyte *), vec_start, diff);
   RESET_VEC(VB->Clip, (GLfloat *), vec_start, diff);
   RESET_VEC(VB->Eye, (GLfloat *), vec_start, diff);
   RESET_VEC(VB->Win, (GLfloat *), vec_start, diff);
   RESET_VEC(VB->BColor, (GLubyte *), vec_start, diff); 
   RESET_VEC(VB->BIndex, &, vec_start, diff);

   if (IM != VB->IM) {
      RESET_VEC(VB->IM->v.Obj, (GLfloat *), vec_start, diff);
      RESET_VEC(VB->IM->v.Normal, (GLfloat *), vec_start, diff);
      RESET_VEC(VB->IM->v.TexCoord[0], (GLfloat *), vec_start, diff);
      RESET_VEC(VB->IM->v.TexCoord[1], (GLfloat *), vec_start, diff);
      RESET_VEC(VB->IM->v.Index, &, vec_start, diff);
      RESET_VEC(VB->IM->v.Elt, &, vec_start, diff);
      RESET_VEC(VB->IM->v.EdgeFlag, &, vec_start, diff);
      RESET_VEC(VB->IM->v.Color, (GLubyte *), vec_start, diff);
   }

   gl_copy_to_current( ctx, IM );

   set_vec_sizes( IM, VB->OrFlag );

   if (MESA_VERBOSE&VERBOSE_IMMEDIATE) 
      fprintf(stderr,
	      "executing cassette, rows %u tc0->size == %u tc1->size == %u\n",
	      VB->Count,
	      VB->TexCoordPtr[0]->size,
	      VB->TexCoordPtr[1]->size);

   if (MESA_VERBOSE&VERBOSE_IMMEDIATE) 
      gl_print_cassette( IM );

   
   if (IM->OrFlag & VERT_EVAL_ANY) 
      gl_eval_vb( VB );

   if (IM->Count > IM->Start || (IM->Flag[IM->Start] & (VERT_END|VERT_BEGIN)))
      fixup_primitives( VB, IM );

   if (VB->IndirectCount > IM->Start) 
      gl_run_pipeline( VB );      
   else 
      gl_update_materials( VB );

   /* This is unfortunate:
    */
   if (VB->pipeline->replay_copied_vertices) {
      if (!VB->CullDone)
	 gl_fast_copy_vb( VB );

      gl_copy_prev_vertices( VB, VB->prev_buffer, IM );
   }

   gl_reset_vb( VB );
}

void gl_print_cassette( struct immediate *IM )
{
   gl_print_cassette_flags( IM, IM->Flag );
}

void gl_print_cassette_flags( struct immediate *IM, GLuint *flags ) 
{
   GLuint i;
   GLuint andflag = IM->AndFlag;
   GLuint orflag = IM->OrFlag;
   GLuint state = IM->BeginState;
   GLuint req = ~0;
   static const char *tplate[5] = { "%s ", 
				    "%s: %f ",
				    "%s: %f %f ",
				    "%s: %f %f %f ",
				    "%s: %f %f %f %f " };

   fprintf(stderr, "Cassette id %d, %u rows.\n", IM->id, IM->Count - IM->Start);

   gl_print_vert_flags("Contains at least one", orflag);

   if (IM->Count != IM->Start) 
   {
      gl_print_vert_flags("Contains a full complement of", andflag);
   
      fprintf(stderr, "Final begin/end state %s/%s, errors %s/%s\n",
	     (state & VERT_BEGIN_0) ? "in" : "out", 
	     (state & VERT_BEGIN_1) ? "in" : "out", 
	     (state & VERT_ERROR_0) ? "y" : "n", 
	     (state & VERT_ERROR_1) ? "y" : "n");

      fprintf(stderr, "Obj size: %u, TexCoord0 size: %u, TexCoord1 size: %u\n",
	     IM->v.Obj.size,
	     IM->v.TexCoord[0].size,
	     IM->v.TexCoord[1].size);
   }

   for (i = IM->Start ; i <= IM->Count ; i++) {
      fprintf(stderr, "%u: ", i);
      if (req & VERT_OBJ_ANY) {
	 if (flags[i] & VERT_EVAL_C1) 
	    fprintf(stderr, "EvalCoord %f ", IM->Obj[i][0]);
	 else if (flags[i] & VERT_EVAL_P1) 
	    fprintf(stderr, "EvalPoint %.0f ", IM->Obj[i][0]);
	 else if (flags[i] & VERT_EVAL_C2) 
	    fprintf(stderr, "EvalCoord %f %f ", IM->Obj[i][0], IM->Obj[i][1]);
	 else if (flags[i] & VERT_EVAL_P2) 
	    fprintf(stderr, "EvalPoint %.0f %.0f ", IM->Obj[i][0], IM->Obj[i][1]);
	 else if (i < IM->Count && (flags[i]&VERT_OBJ_234)) {
	    fprintf(stderr, "(%x) ", flags[i] & VERT_OBJ_234);
	    fprintf(stderr, tplate[vertex_sizes[(flags[i]&VERT_OBJ_234)<<1]],
		   "Obj",
		   IM->Obj[i][0], IM->Obj[i][1], IM->Obj[i][2], IM->Obj[i][3]);
	 }
      }

      if (req & flags[i] & VERT_ELT) 
	 fprintf(stderr, " Elt %u\t", IM->Elt[i]);

      if (req & flags[i] & VERT_NORM)
	 fprintf(stderr, " Norm %f %f %f ", 
		IM->Normal[i][0], IM->Normal[i][1], IM->Normal[i][2]);

      if (req & flags[i] & VERT_TEX0_ANY)
	 fprintf(stderr, tplate[vertex_sizes[(flags[i]>>VERT_TEX0_SHIFT)&7]], 
		"TC0",
		IM->TexCoord[0][i][0], IM->TexCoord[0][i][1], 
		IM->TexCoord[0][i][2], IM->TexCoord[0][i][2]);


      if (req & flags[i] & VERT_TEX1_ANY)
	 fprintf(stderr, tplate[vertex_sizes[(flags[i]>>(VERT_TEX0_SHIFT+4))&7]], 
		"TC1",
		IM->TexCoord[1][i][0], IM->TexCoord[1][i][1], 
		IM->TexCoord[1][i][2], IM->TexCoord[1][i][2]);

      if (req & flags[i] & VERT_RGBA)
	 fprintf(stderr, " Rgba %d %d %d %d ", 
		IM->Color[i][0], IM->Color[i][1], 
		IM->Color[i][2], IM->Color[i][3]);

      if (req & flags[i] & VERT_INDEX)
	 fprintf(stderr, " Index %u ", IM->Index[i]);
      
      if (req & flags[i] & VERT_EDGE)
	 fprintf(stderr, " Edgeflag %d ", IM->EdgeFlag[i]);

      if (req & flags[i] & VERT_MATERIAL)
	 fprintf(stderr, " Material ");
	 

      /* The order of these two is not easily knowable, but this is
       * the usually correct way to look at them.
       */
      if (req & flags[i] & VERT_END) 
	 fprintf(stderr, " END ");

      if (req & flags[i] & VERT_BEGIN) 
	 fprintf(stderr, " BEGIN(%s) ", gl_prim_name[IM->Primitive[i]]);

      fprintf(stderr, "\n");  
   }      
}













