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

/*
 * 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.
 *
 *
 * 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 "fxdrv.h"
#include "mmath.h"
#include "macros.h"
#include "vector.h"
#include "types.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)						\
do {								\
   GLuint out = in ^ 1;						\
   GLfloat **indata = inlist[in];				\
   GrVertex **inverts = vertlist[in];				\
   GrVertex **outverts = vertlist[out];				\
   GLfloat **outdata = inlist[in = out];			\
   GLfloat *J = indata[n-1];					\
   GLfloat dpJ = (sgn J[v]) + J[3];				\
   GLuint nr = n;						\
								\
   for (i = n = 0 ; i < nr ; i++) {				\
      GLfloat *I = indata[i];					\
      GLfloat dpI = (sgn I[v]) + I[3];				\
								\
      if (DIFFERENT_SIGNS(dpI, dpJ)) {				\
	 GLuint j;						\
         GLfloat t = dpI / (dpI - dpJ);                         \
	 outverts[n] = 0;					\
	 outdata[n++] = store;					\
	 store[3] = LINTERP(t, I[3], J[3]);			\
	 store[v] = - sgn store[3];				\
	 if (v != 0) store[0] = LINTERP(t, I[0], J[0]);		\
	 if (v != 1) store[1] = LINTERP(t, I[1], J[1]);		\
	 if (v != 2) store[2] = LINTERP(t, I[2], J[2]);		\
	 store += 4;						\
	 for (j = 4 ; j < sz ; j+=4,store+=4) {			\
	    store[0] = LINTERP(t, I[j],   J[j] );		\
	    store[1] = LINTERP(t, I[j+1], J[j+1] );		\
	    store[2] = LINTERP(t, I[j+2], J[j+2] );		\
	    store[3] = LINTERP(t, I[j+3], J[j+3] );		\
	 }							\
      }								\
								\
      if (!NEGATIVE(dpI)) {					\
	 outverts[n] = inverts[i];				\
	 outdata[n++] = I;					\
      }								\
								\
								\
      J = I;							\
      dpJ = dpI;						\
   }								\
								\
   if (n < 3) return 0;						\
} while (0)


/* Originally used this for the viewclip planes as well, as in
 * CLIP(-1,0,0,1), which was just as fast, but tended to lead to
 * cracks.  I haven't figured out exactly why this is, but the above
 * code only really differs in the way it sets store[v] to +- w.
 */
#define UCLIP(a,b,c,d)					\
do {							\
   GLuint out = in ^ 1;					\
   GLfloat **indata = inlist[in];			\
   GrVertex **inverts = vertlist[in];			\
   GrVertex **outverts = vertlist[out];			\
   GLfloat **outdata = inlist[in = out];		\
   GLfloat *J = indata[n-1];				\
   GLfloat dpJ = DOT4V(J,a,b,c,d);			\
   GLuint nr = n;					\
							\
   for (i = n = 0 ; i < nr ; i++) {			\
      GLfloat *I = indata[i];				\
      GLfloat dpI = DOT4V(I,a,b,c,d);			\
							\
      if (DIFFERENT_SIGNS(dpI, dpJ)) {			\
	 GLuint j;					\
	 GLfloat t = dpI / (dpI - dpJ);			\
	 outverts[n] = 0;				\
	 outdata[n++] = store;				\
	 for (j = 0 ; j < sz ; j+=4,store+=4) {		\
	    store[0] = LINTERP(t, I[j],   J[j] );	\
	    store[1] = LINTERP(t, I[j+1], J[j+1] );	\
	    store[2] = LINTERP(t, I[j+2], J[j+2] );	\
	    store[3] = LINTERP(t, I[j+3], J[j+3] );	\
	 }						\
      }							\
							\
      if (!NEGATIVE(dpI)) {				\
	 outverts[n] = inverts[i];			\
	 outdata[n++] = I;				\
      }							\
							\
							\
      J = I;						\
      dpJ = dpI;					\
   }							\
							\
   if (n < 3) return 0;					\
} while (0)



/* Data parameter is organized as 4 clip coordinates followed by an
 * arbitary number (sz-4) of additional data.  The three original
 * vertices are packed together at the start, and there is room for at
 * least VB_MAX_CLIPPED_VERTS vertices of the same size in this
 * storage.
 *
 */
static INLINE GLuint fx_clip_triangle( GLcontext *ctx,
				       GLfloat *inoutlist[],
				       GrVertex **verts,
				       GLuint sz,
				       GLuint mask )
{
   GLuint n = 3;
   GLfloat *store = inoutlist[n-1] + sz;
   GLfloat *vlist[VB_MAX_CLIPPED_VERTS];
   GrVertex *verts2[VB_MAX_CLIPPED_VERTS];
   GLfloat **inlist[2];
   GrVertex **vertlist[2];
   GLuint in = 0;
   GLuint i;

   inlist[0] = inoutlist;
   inlist[1] = vlist;

   vertlist[0] = verts;
   vertlist[1] = verts2;

   if (mask & CLIP_ALL_BITS)
   {
      if (mask & CLIP_RIGHT_BIT)
	 CLIP(-,0);

      if (mask & CLIP_LEFT_BIT)
	 CLIP(+,0);

      if (mask & CLIP_TOP_BIT)
	 CLIP(-,1);

      if (mask & CLIP_BOTTOM_BIT)
	 CLIP(+,1);

      if (mask & CLIP_FAR_BIT)
	 CLIP(-,2);

      if (mask & CLIP_NEAR_BIT)
	 CLIP(+,2);
   }

   if (mask & CLIP_USER_BIT) {
      GLuint bit;
      GLfloat (*plane)[4] = &ctx->Transform.ClipUserPlane[0];
      for (bit = 0x100 ; bit < mask ; plane++, bit *= 2) {
	 if (mask & bit) {
	    GLfloat a = plane[0][0];
	    GLfloat b = plane[0][1];
	    GLfloat c = plane[0][2];
	    GLfloat d = plane[0][3];
	    UCLIP(a,b,c,d);
	 }
      }
   }

   if (inlist[in] != inoutlist)
      for (i = 0 ; i < n ; i++) {
	 inoutlist[i] = inlist[in][i];
	 verts[i] = verts2[i];
      }

   return n;
}



static INLINE GLuint fx_view_clip_triangle( GLcontext *ctx,
					    GLfloat *inoutlist[],
					    GrVertex **verts,
					    GLuint sz,
					    GLubyte mask )
{
   GLuint n = 3;
   GLfloat *store = inoutlist[n-1] + sz;
   GLfloat *vlist[VB_MAX_CLIPPED_VERTS];
   GrVertex *verts2[VB_MAX_CLIPPED_VERTS];
   GLfloat **inlist[2];
   GrVertex **vertlist[2];
   GLuint in = 0;
   GLuint i;

   inlist[0] = inoutlist;
   inlist[1] = vlist;

   vertlist[0] = verts;
   vertlist[1] = verts2;

   if (mask & CLIP_RIGHT_BIT)
      CLIP(-,0);

   if (mask & CLIP_LEFT_BIT)
      CLIP(+,0);

   if (mask & CLIP_TOP_BIT)
      CLIP(-,1);

   if (mask & CLIP_BOTTOM_BIT)
      CLIP(+,1);

   if (mask & CLIP_FAR_BIT)
      CLIP(-,2);

   if (mask & CLIP_NEAR_BIT)
      CLIP(+,2);

   if (inlist[in] != inoutlist)
      for (i = 0 ; i < n ; i++) {
	 inoutlist[i] = inlist[in][i];
	 verts[i] = verts2[i];
      }

   return n;
}



#undef CLIP

#define CLIP(x,y,z,w)					\
do {							\
   GLfloat dpI = DOT4V(I,x,y,z,w);			\
   GLfloat dpJ = DOT4V(J,x,y,z,w);			\
							\
   if (DIFFERENT_SIGNS(dpI, dpJ)) {			\
      GLuint j;						\
      GLfloat t = dpI / (dpI - dpJ); 			\
      GLfloat *tmp = store;				\
							\
      for (j = 0 ; j < sz ; j+=2) {			\
	 *store++ = LINTERP(t, I[j],   J[j] );		\
	 *store++ = LINTERP(t, I[j+1], J[j+1] );	\
      }							\
							\
      if (NEGATIVE(dpI)) 				\
	 I = tmp;					\
      else						\
	 J = tmp;					\
							\
   } 							\
   else if (NEGATIVE(dpI))				\
      return 0;						\
							\
} while (0)


static GLuint fx_clip_line( GLcontext *ctx,
			    GLfloat *inoutlist[],
			    GLuint sz,
			    GLubyte clipor )
{
   GLfloat *I = inoutlist[0];
   GLfloat *J = inoutlist[1];
   GLfloat *store = J + sz;

   if (clipor & CLIP_ALL_BITS)
   {
      if (clipor & CLIP_LEFT_BIT)
	 CLIP(1,0,0,1);
 
      if (clipor & CLIP_RIGHT_BIT)
	 CLIP(-1,0,0,1);
 
      if (clipor & CLIP_TOP_BIT)
	 CLIP(0,-1,0,1);
 
      if (clipor & CLIP_BOTTOM_BIT)
	 CLIP(0,1,0,1);
 
      if (clipor & CLIP_FAR_BIT)
	 CLIP(0,0,-1,1);
 
      if (clipor & CLIP_NEAR_BIT)
	 CLIP(0,0,1,1);
   }

   if (clipor & CLIP_USER_BIT) {
      GLuint i;
      for (i = 0 ; i < MAX_CLIP_PLANES ; i++) {
	 if (ctx->Transform.ClipEnabled[i]) {
	    GLfloat a = ctx->Transform.ClipUserPlane[i][0];
	    GLfloat b = ctx->Transform.ClipUserPlane[i][1];
	    GLfloat c = ctx->Transform.ClipUserPlane[i][2];
	    GLfloat d = ctx->Transform.ClipUserPlane[i][3];
	    CLIP(a,b,c,d);
	 }
      }
   }

   inoutlist[0] = I;
   inoutlist[1] = J;

   return 2;
}






#if defined(FX_V2)

#define VARS_XYZW				\
  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_XYZW				\
{						\
  GLfloat oow = 1.0 / data[3];			\
  v->x   = data[0]*oow*vsx + vtx;		\
  v->y   = data[1]*oow*vsy + vty;		\
  v->ooz = data[2]*oow*vsz + vtz;		\
  v->oow = oow; 				\
}
#else

#if defined(DRIVERTS)

#define VARS_XYZW				\
  GLfloat vsx = mat[MAT_SX];			\
  GLfloat vsy = mat[MAT_SY];			\
  GLfloat vsz = mat[MAT_SZ];			\
  GLfloat vtx = mat[MAT_TX]+fxMesa->x_offset;	\
  GLfloat vty = mat[MAT_TY]+fxMesa->y_delta;	\
  GLfloat vtz = mat[MAT_TZ];

#define DO_SETUP_XYZW				\
{						\
  GLfloat oow = 1.0 / data[3];			\
  v->x   = data[0]*oow*vsx + vtx;		\
  v->y   = data[1]*oow*vsy + vty;		\
  v->ooz = data[2]*oow*vsz + vtz;		\
  v->oow = oow;					\
}

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

#define DO_SETUP_XYZW				\
{						\
  GLfloat oow = 1.0 / data[3];			\
  v->x = data[0]*oow*vsx + snap_tx;		\
  v->y = data[1]*oow*vsy + snap_ty;		\
  v->ooz = data[2]*oow*vsz + vtz;		\
  v->oow = oow;					\
  v->x -= snapper;				\
  v->y -= snapper;				\
}

#endif
#endif

#define COPY_XYZW_STRIDE				\
  { GLfloat *clip = VEC_ELT(VB->ClipPtr, GLfloat, e);	\
    COPY_4FV(out, clip); }

#define VARS_RGBA

#define COPY_RGBA_STRIDE					\
  { GLubyte *color = VEC_ELT(VB->ColorPtr, GLubyte, e);		\
    UBYTE_RGBA_TO_FLOAT_255_RGBA((out+4), color); }

#if FX_USE_PARGB
#define DO_SETUP_RGBA				\
  GET_PARGB(v) = (((int)data[4+3]) << 24) | 	\
  		 (((int)data[4+0]) << 16) | 	\
  		 (((int)data[4+1]) << 8)  |	\
  		 (((int)data[4+2]) << 0);	
#else
#define DO_SETUP_RGBA				\
  v->r = data[4+0];				\
  v->g = data[4+1];				\
  v->b = data[4+2];				\
  v->a = data[4+3];
#endif /* FX_USE_PARGB */

#define VARS_TMU0							\
  struct gl_texture_unit *t0 = &ctx->Texture.Unit[tmu0_source];		\
  GLfloat sScale0 = fxTMGetTexInfo(t0->Current)->sScale;	\
  GLfloat tScale0 = fxTMGetTexInfo(t0->Current)->tScale;	\


#define COPY_TMU0_STRIDE(offset)					\
  { GLfloat *tc0 = VEC_ELT(tc0_vec, GLfloat, e);	\
    COPY_2V((out+offset), tc0); }


#define DO_SETUP_TMU0(offset)				\
  v->tmuvtx[0].sow = data[offset+0]*sScale0*v->oow;	\
  v->tmuvtx[0].tow = data[offset+1]*tScale0*v->oow;

#define VARS_TMU1							\
  struct gl_texture_unit *t1 = &ctx->Texture.Unit[tmu1_source];		\
  GLfloat sScale1 = fxTMGetTexInfo(t1->Current)->sScale;	\
  GLfloat tScale1 = fxTMGetTexInfo(t1->Current)->tScale;

#define COPY_TMU1_STRIDE(offset)					\
  { GLfloat *tc1 = VEC_ELT(tc1_vec, GLfloat, e);	\
    COPY_2V((out+offset), tc1); }


#define DO_SETUP_TMU1(offset)				\
  v->tmuvtx[1].sow = data[offset+0]*sScale1*v->oow;	\
  v->tmuvtx[1].tow = data[offset+1]*tScale1*v->oow;

#define COPY_NIL(offset) ASSIGN_2V((out+offset), 0, 0);

#define IND 0
#define TAG(x) x##_nil
#include "fxcliptmp.h"

#define IND SETUP_RGBA
#define TAG(x) x##_RGBA
#include "fxcliptmp.h"

#define IND SETUP_TMU0
#define TAG(x) x##_TMU0
#include "fxcliptmp.h"

#define IND (SETUP_TMU0|SETUP_TMU1)
#define TAG(x) x##_TMU0_TMU1
#include "fxcliptmp.h"

#define IND (SETUP_RGBA|SETUP_TMU0)
#define TAG(x) x##_RGBA_TMU0
#include "fxcliptmp.h"

#define IND (SETUP_RGBA|SETUP_TMU0|SETUP_TMU1)
#define TAG(x) x##_RGBA_TMU0_TMU1
#include "fxcliptmp.h"

tfxTriViewClipFunc fxTriViewClipTab[0x8];
tfxTriClipFunc fxTriClipStrideTab[0x8];
tfxLineClipFunc fxLineClipTab[0x8];


void fxDDClipInit()
{
   fxTriViewClipTab[0] = fx_tri_view_clip_nil;
   fxTriViewClipTab[SETUP_RGBA] = fx_tri_view_clip_RGBA;
   fxTriViewClipTab[SETUP_TMU0] = fx_tri_view_clip_TMU0;
   fxTriViewClipTab[SETUP_TMU0|SETUP_TMU1] = fx_tri_view_clip_TMU0_TMU1;
   fxTriViewClipTab[SETUP_RGBA|SETUP_TMU0] = fx_tri_view_clip_RGBA_TMU0;
   fxTriViewClipTab[SETUP_RGBA|SETUP_TMU0|SETUP_TMU1] = fx_tri_view_clip_RGBA_TMU0_TMU1;

   fxTriClipStrideTab[0] = fx_tri_clip_stride_nil;
   fxTriClipStrideTab[SETUP_RGBA] = fx_tri_clip_stride_RGBA;
   fxTriClipStrideTab[SETUP_TMU0] = fx_tri_clip_stride_TMU0;
   fxTriClipStrideTab[SETUP_TMU0|SETUP_TMU1] = fx_tri_clip_stride_TMU0_TMU1;
   fxTriClipStrideTab[SETUP_RGBA|SETUP_TMU0] = fx_tri_clip_stride_RGBA_TMU0;
   fxTriClipStrideTab[SETUP_RGBA|SETUP_TMU0|SETUP_TMU1] = fx_tri_clip_stride_RGBA_TMU0_TMU1;

   fxLineClipTab[0] = fx_line_clip_nil;
   fxLineClipTab[SETUP_RGBA] = fx_line_clip_RGBA;
   fxLineClipTab[SETUP_TMU0] = fx_line_clip_TMU0;
   fxLineClipTab[SETUP_TMU0|SETUP_TMU1] = fx_line_clip_TMU0_TMU1;
   fxLineClipTab[SETUP_RGBA|SETUP_TMU0] = fx_line_clip_RGBA_TMU0;
   fxLineClipTab[SETUP_RGBA|SETUP_TMU0|SETUP_TMU1] = fx_line_clip_RGBA_TMU0_TMU1;
}

#else

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

#endif
