/* -*- mode: C; tab-width:8; c-basic-offset:2 -*- */

/*
 * Mesa 3-D graphics library
 * Version:  3.3
 *
 * Copyright (C) 1999-2000  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.
 *
 *
 * Original Mesa / 3Dfx device driver (C) 1999 David Bucciarelli, by the
 * terms stated above.
 *
 * Thank you for your contribution, David!
 *
 * Please make note of the above copyright/license statement.  If you
 * contributed code or bug fixes to this code under the previous (GNU
 * Library) license and object to the new license, your code will be
 * removed at your request.  Please see the Mesa docs/COPYRIGHT file
 * for more information.
 *
 * Additional Mesa/3Dfx driver developers:
 *   Daryll Strauss <daryll@precisioninsight.com>
 *   Keith Whitwell <keith@precisioninsight.com>
 *
 * See fxapi.h for more revision/author details.
 */


#ifdef HAVE_CONFIG_H
#include "conf.h"
#endif


#if defined(FX)

#include "types.h"
#include "cva.h"
#include "mmath.h"
#include "fxdrv.h"
#include "vertices.h"
#include "X86/common_x86_asm.h"


#if 0 && defined(__i386__)
#define NEGATIVE(f)          ((*(int *)&f) < 0)
#define DIFFERENT_SIGNS(a,b) (((*(int *)&a)^(*(int *)&b)) < 0)
#else
#define NEGATIVE(f)          (f < 0)
#define DIFFERENT_SIGNS(a,b) ((a*b) < 0)
#endif

#define LINTERP( T, A, B )   ( (A) + (T) * ( (B) - (A) ) )


#define CLIP(sgn,v,PLANE)					\
if (mask & PLANE) {						\
   GLuint *indata = inlist[in];					\
   GLuint *outdata = inlist[in ^= 1];				\
   GLuint nr = n;						\
   GLfloat *J = verts[indata[nr-1]].f;				\
   GLfloat dpJ = (sgn J[v]) + J[CLIP_WCOORD];			\
								\
   inlist[0] = vlist1;						\
   for (i = n = 0 ; i < nr ; i++) {				\
      GLuint elt_i = indata[i];					\
      GLfloat *I = verts[elt_i].f;				\
      GLfloat dpI = (sgn I[v]) + I[CLIP_WCOORD];		\
								\
      if (DIFFERENT_SIGNS(dpI, dpJ)) {				\
	 GLfloat *O = verts[next_vert].f;			\
         GLfloat t = dpI / (dpI - dpJ);				\
	 GLuint j;						\
								\
	 clipmask[next_vert] = 0;				\
	 outdata[n++] = next_vert++;				\
								\
         for (j = 0 ; j < SIZE ; j += 2) {			\
	    O[j]   = LINTERP(t, I[j],   J[j]);			\
	    O[j+1] = LINTERP(t, I[j+1], J[j+1]);	       	\
	 }							\
      }								\
								\
      clipmask[elt_i] |= PLANE;		/* don't set up */	\
								\
      if (!NEGATIVE(dpI)) {					\
	 outdata[n++] = elt_i;					\
	 clipmask[elt_i] &= ~PLANE;	/* set up after all */	\
      }								\
								\
      J = I;							\
      dpJ = dpI;						\
   }								\
								\
   if (n < 3) return;						\
}

#define LINE_CLIP(x,y,z,w,PLANE)				\
if (mask & PLANE) {						\
   GLfloat dpI = DOT4V(I,x,y,z,w);				\
   GLfloat dpJ = DOT4V(J,x,y,z,w);				\
								\
   if (DIFFERENT_SIGNS(dpI, dpJ)) {				\
      GLfloat *O = verts[next_vert].f;				\
      GLfloat t = dpI / (dpI - dpJ);				\
      GLuint j;							\
								\
      for (j = 0 ; j < SIZE ; j += 2) {				\
         O[j]   = LINTERP(t, I[j],   J[j]);			\
	 O[j+1] = LINTERP(t, I[j+1], J[j+1]);	       		\
      }								\
								\
      clipmask[next_vert] = 0;					\
								\
      if (NEGATIVE(dpI)) {					\
  	 clipmask[elts[0]] |= PLANE;				\
	 I = O; elts[0] = next_vert++;				\
      } else {							\
  	 clipmask[elts[1]] |= PLANE;				\
	 J = O;	elts[1] = next_vert++;				\
      }								\
   }								\
   else if (NEGATIVE(dpI))					\
      return;							\
}


#define CLIP_POINT( e )				\
   if (mask[e])					\
      *out++ = e

#define CLIP_LINE( e1, e0 )						\
do {									\
   GLubyte ormask = mask[e0] | mask[e1];				\
   out[0] = e1;								\
   out[1] = e0;								\
   out+=2;								\
   if (ormask) {							\
      out-=2;								\
      if (!(mask[e0] & mask[e1])) {					\
	 TAG(fx_line_clip)( &out, verts, mask, &next_vert, ormask);	\
      }									\
   }									\
} while (0)

#define CLIP_TRIANGLE( e2, e1, e0 )					\
do {									\
   GLubyte ormask;							\
   out[0] = e2;								\
   out[1] = e1;								\
   out[2] = e0;								\
   out += 3;								\
   ormask = mask[e2] | mask[e1] | mask[e0];				\
   if (ormask) {							\
      out -= 3;								\
      if ( !(mask[e2] & mask[e1] & mask[e0])) {				\
	 TAG(fx_tri_clip)( &out, verts, mask, &next_vert, ormask );	\
      }									\
   }									\
} while (0)

#if defined(FX_V2) || defined(DRIVERTS)

#define VARS_XYZ				\
  GLfloat vsx = mat[MAT_SX];			\
  GLfloat vsy = mat[MAT_SY];			\
  GLfloat vsz = mat[MAT_SZ];			\
  GLfloat vtx = mat[MAT_TX];			\
  GLfloat vty = mat[MAT_TY];			\
  GLfloat vtz = mat[MAT_TZ];

#define DO_SETUP_XYZ				\
  f[XCOORD] = f[0] * oow * vsx + vtx;		\
  f[YCOORD] = f[1] * oow * vsy + vty;		\
  f[ZCOORD] = f[2] * oow * vsz + vtz;

#else
#if defined(HAVE_FAST_MATH)

#define VARS_XYZ				\
  GLfloat vsx = mat[MAT_SX];			\
  GLfloat vsy = mat[MAT_SY];			\
  GLfloat vsz = mat[MAT_SZ];			\
  const GLfloat snapper = (3L << 18);		\
  GLfloat vtx = mat[MAT_TX] + snapper;		\
  GLfloat vty = mat[MAT_TY] + snapper;		\
  GLfloat vtz = mat[MAT_TZ];

#define DO_SETUP_XYZ				\
  f[XCOORD] = f[0] * oow * vsx + vtx;		\
  f[XCOORD] -= snapper;				\
  f[YCOORD] = f[1] * oow * vsy + vty;		\
  f[YCOORD] -= snapper;				\
  f[ZCOORD] = f[2] * oow * vsz + vtz;

#else

#define VARS_XYZ				\
  GLfloat vsx = mat[MAT_SX] * 16.0f;		\
  GLfloat vsy = mat[MAT_SY] * 16.0f;		\
  GLfloat vsz = mat[MAT_SZ];			\
  GLfloat vtx = mat[MAT_TX] * 16.0f;		\
  GLfloat vty = mat[MAT_TY] * 16.0f;		\
  GLfloat vtz = mat[MAT_TZ];

#define DO_SETUP_XYZ					\
  f[XCOORD] = ((int)(f[0]*oow*vsx+vtx)) * (1.0f/16.0f);	\
  f[YCOORD] = ((int)(f[1]*oow*vsy+vty)) * (1.0f/16.0f);	\
  f[ZCOORD] = f[2]*oow*vsz + vtz;


#endif
#endif



struct fx_fast_tab
{
   void (*build_vertices)( struct vertex_buffer *VB, GLuint do_clip );

   void (*clip[GL_POLYGON+1])( struct vertex_buffer *VB,
			       GLuint start,
			       GLuint count,
			       GLuint parity );

   void (*project_clipped_vertices)( GLfloat *first,
				     GLfloat *last,
				     const GLfloat *mat,
				     GLuint stride,
				     const GLubyte *mask );

   void (*project_vertices)( GLfloat *first,
			     GLfloat *last,
			     const GLfloat *mat,
			     GLuint stride );
};

/* Pack either rgba or texture into the remaining half of a 32 byte vertex.
 */
#define CLIP_R  CLIP_RCOORD
#define CLIP_G  CLIP_GCOORD
#define CLIP_B  CLIP_BCOORD
#define CLIP_A  CLIP_ACOORD
#define CLIP_S0 4
#define CLIP_T0 5
#define CLIP_S1 6
#define CLIP_T1 7

#define SIZE 4
#define TYPE (0)
#define TAG(x) x
#include "fxfasttmp.h"

#define SIZE 8
#define TYPE (SETUP_RGBA)
#define TAG(x) x##_RGBA
#include "fxfasttmp.h"

#define SIZE 6
#define TYPE (SETUP_TMU0)
#define TAG(x) x##_TMU0
#include "fxfasttmp.h"

#define SIZE 8
#define TYPE (SETUP_TMU0|SETUP_TMU1)
#define TAG(x) x##_TMU0_TMU1
#include "fxfasttmp.h"

#undef CLIP_S1
#undef CLIP_T1
#define CLIP_S1 4
#define CLIP_T1 5

#define SIZE 6
#define TYPE (SETUP_TMU1)
#define TAG(x) x##_TMU1
#include "fxfasttmp.h"

/* These three need to use a full 64 byte clip-space vertex.
 */
#undef CLIP_S0
#undef CLIP_T0
#undef CLIP_S1
#undef CLIP_T1

#define CLIP_S0 8
#define CLIP_T0 9
#define CLIP_S1 10
#define CLIP_T1 11

#define SIZE 10
#define TYPE (SETUP_RGBA|SETUP_TMU0)
#define TAG(x) x##_RGBA_TMU0
#include "fxfasttmp.h"

#define SIZE 12
#define TYPE (SETUP_RGBA|SETUP_TMU0|SETUP_TMU1)
#define TAG(x) x##_RGBA_TMU0_TMU1
#include "fxfasttmp.h"

#undef CLIP_S1
#undef CLIP_T1
#define CLIP_S1 8
#define CLIP_T1 9

#define SIZE 10
#define TYPE (SETUP_RGBA|SETUP_TMU1)
#define TAG(x) x##_RGBA_TMU1
#include "fxfasttmp.h"

static struct fx_fast_tab fxFastTab[0x8];

void fxDDFastPathInit()
{
   fx_init_fastpath( &fxFastTab[0] );
   fx_init_fastpath_RGBA( &fxFastTab[SETUP_RGBA] );
   fx_init_fastpath_TMU0( &fxFastTab[SETUP_TMU0] );
   fx_init_fastpath_TMU1( &fxFastTab[SETUP_TMU1] );
   fx_init_fastpath_RGBA_TMU0( &fxFastTab[SETUP_RGBA|SETUP_TMU0] );
   fx_init_fastpath_RGBA_TMU1( &fxFastTab[SETUP_RGBA|SETUP_TMU1] );
   fx_init_fastpath_TMU0_TMU1( &fxFastTab[SETUP_TMU0|SETUP_TMU1] );
   fx_init_fastpath_RGBA_TMU0_TMU1( &fxFastTab[SETUP_RGBA|SETUP_TMU0|
					       SETUP_TMU1] );
}



void fxDDFastPath( struct vertex_buffer *VB )
{
   GLcontext *ctx = VB->ctx;
   GLenum prim = ctx->CVA.elt_mode;
   struct tfxMesaContext *fxMesa = FX_CONTEXT(ctx);
   struct fx_fast_tab *tab = &fxFastTab[fxMesa->setupindex & 0x7];
   GLuint do_clip = 1;
   struct tfxMesaVertexBuffer *fxVB = FX_DRIVER_DATA(VB);
#ifdef DRIVERTS
   GLfloat tx, ty;
#endif

   fxVertex *first;
   GLfloat *mat = ctx->Viewport.WindowMap.m;

   gl_prepare_arrays_cva( VB );	                /* still need this */

   if (VB->EltPtr->count * 12 > fxVB->size) {
      fxDDResizeVB( VB, VB->EltPtr->count * 12 );
      do_clip = 1;
   }

   tab->build_vertices( VB, do_clip );          /* object->clip space */

   first = FX_DRIVER_DATA(VB)->verts;

#ifdef DRIVERTS
   tx=mat[MAT_TX];
   ty=mat[MAT_TY];
   mat[MAT_TX]=tx+fxMesa->x_offset;
   mat[MAT_TY]=ty+fxMesa->y_delta;
#endif

   if (VB->ClipOrMask) {
      if (!VB->ClipAndMask) {
	 GLubyte tmp = VB->ClipOrMask;

	 tab->clip[prim]( VB, 0, VB->EltPtr->count, 0 );   /* clip */

	 tab->project_clipped_vertices( fxVB->verts->f,
					fxVB->last_vert->f,
					mat, 16 * 4,
					VB->ClipMask );

	 ctx->CVA.elt_mode = gl_reduce_prim[prim];
	 VB->EltPtr = &(FX_DRIVER_DATA(VB)->clipped_elements);

	 VB->ClipOrMask = 0;
	 fxDDRenderElementsDirect( VB );        /* render using new list */
	 VB->ClipOrMask = tmp;
      }
   } else {
      tab->project_vertices(  fxVB->verts->f,
			      fxVB->last_vert->f,
			      mat, 16 * 4 );

      fxDDRenderElementsDirect( VB );           /* render using orig list */
   }

#ifdef DRIVERTS
   mat[MAT_TX]=tx;
   mat[MAT_TY]=ty;
#endif

   /* This indicates that there is no cached data to reuse.
    */
   VB->pipeline->data_valid = 0;
   VB->pipeline->pipeline_valid = 0;
}


#else

/*
 * Need this to provide at least one external definition.
 */
int gl_fxfastpath_dummy(void)
{
  return 0;
}

#endif
