/* $XFree86*/ /* -*- c-basic-offset: 3 -*- */
/*
 * Copyright 2005 Eric Anholt
 * 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 (including the next
 * paragraph) 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
 * THE AUTHORS OR COPYRIGHT HOLDERS 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.
 *
 * Authors:
 *    Eric Anholt <anholt@FreeBSD.org>
 *    Jim Duchek <jim@linuxpimps.com>	-- Utah GLX 6326 code
 *    Alan Cox <alan@redhat.com>	-- 6326 Debugging
 *
 */

static void TAG(sis_draw_tri_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;
   sisVertexPtr v1 = (sisVertexPtr)(verts + smesa->vertex_size * 4);
   sisVertexPtr v2 = (sisVertexPtr)(verts + smesa->vertex_size * 4 * 2);

   mWait3DCmdQueue (MMIO_VERT_REG_COUNT * 3);
   SIS_MMIO_WRITE_VERTEX(v0, 0, 0);
   SIS_MMIO_WRITE_VERTEX(v1, 1, 0);
   SIS_MMIO_WRITE_VERTEX(v2, 2, 1);
}

static void TAG(sis_draw_line_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;
   sisVertexPtr v1 = (sisVertexPtr)(verts + smesa->vertex_size * 4);

   mWait3DCmdQueue (MMIO_VERT_REG_COUNT * 2);
   SIS_MMIO_WRITE_VERTEX(v0, 0, 0);
   SIS_MMIO_WRITE_VERTEX(v1, 1, 1);
}

static void TAG(sis_draw_point_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;

   mWait3DCmdQueue (MMIO_VERT_REG_COUNT * 1);
   SIS_MMIO_WRITE_VERTEX(v0, 1, 1);
}

#if !(SIS_STATES & VERT_UV1)
static void TAG(sis6326_draw_tri_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;
   sisVertexPtr v1 = (sisVertexPtr)(verts + smesa->vertex_size * 4);
   sisVertexPtr v2 = (sisVertexPtr)(verts + smesa->vertex_size * 4 * 2);
   GLfloat x0, x1, x2;
   GLfloat y0, y1, y2;
   GLfloat delt02, diffx02, diffy02, diffy12;
   GLint dwPrimitiveSet = smesa->dwPrimitiveSet;
   sisVertex tv0, tv1, tv2;
   
   /* XXX Culling? */

   tv0 = *v0;
   tv1 = *v1;
   tv2 = *v2;
   tv0.v.y = Y_FLIP(tv0.v.y);
   tv1.v.y = Y_FLIP(tv1.v.y);
   tv2.v.y = Y_FLIP(tv2.v.y);
   v0 = &tv0;
   v1 = &tv1;
   v2 = &tv2;

   /* Cull polygons we won't draw. The hardware draws funky things if it
      is fed these */
   if((((v1->v.x - v0->v.x) * (v0->v.y - v2->v.y)) +
       ((v1->v.y - v0->v.y) * (v2->v.x - v0->v.x))) < 0)
      return;
   y0 = v0->v.y;
   y1 = v1->v.y;
   y2 = v2->v.y;
   

   if (y0 > y1) {
      if (y1 > y2) {
         x0 = v0->v.x;
         x1 = v1->v.x;
         x2 = v2->v.x;
         dwPrimitiveSet |= OP_6326_3D_ATOP | OP_6326_3D_BMID | OP_6326_3D_CBOT;
         if ((SIS_STATES & VERT_SMOOTH) == 0)
            dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_BOT;
      } else {
         if (y0 > y2) {
            x0 = v0->v.x;
            x1 = v2->v.x;
            y1 = v2->v.y;
            dwPrimitiveSet |= OP_6326_3D_ATOP | OP_6326_3D_CMID |
                OP_6326_3D_BBOT;
            if ((SIS_STATES & VERT_SMOOTH) == 0)
               dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_MID;
         } else {
            x0 = v2->v.x;
            y0 = v2->v.y;
            x1 = v0->v.x;
            y1 = v0->v.y;
            dwPrimitiveSet |= OP_6326_3D_CTOP | OP_6326_3D_AMID |
                OP_6326_3D_BBOT;
            if ((SIS_STATES & VERT_SMOOTH) == 0)
               dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_TOP;
         }
         x2 = v1->v.x;
         y2 = v1->v.y;
      }
   } else {
      if (y0 > y2) {
         x0 = v1->v.x;
         y0 = v1->v.y;
         x1 = v0->v.x;
         y1 = v0->v.y;
         x2 = v2->v.x;
         dwPrimitiveSet |= OP_6326_3D_BTOP | OP_6326_3D_AMID | OP_6326_3D_CBOT;
         if ((SIS_STATES & VERT_SMOOTH) == 0)
            dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_BOT;
      } else {
         if (y1 > y2) {
            x0 = v1->v.x;
            y0 = v1->v.y;
            x1 = v2->v.x;
            y1 = v2->v.y;
            dwPrimitiveSet |= OP_6326_3D_BTOP | OP_6326_3D_CMID |
                OP_6326_3D_ABOT;
            if ((SIS_STATES & VERT_SMOOTH) == 0)
               dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_MID;
         } else {
            x0 = v2->v.x;
            y0 = v2->v.y;
            x1 = v1->v.x;
            dwPrimitiveSet |= OP_6326_3D_CTOP | OP_6326_3D_BMID |
                OP_6326_3D_ABOT;
            if ((SIS_STATES & VERT_SMOOTH) == 0)
               dwPrimitiveSet |= OP_6326_3D_SHADE_FLAT_TOP;
         }
         x2 = v0->v.x;
         y2 = v0->v.y;
      }
   }

   if (x1 <= x0 && x1 <= x2) {
      dwPrimitiveSet |= OP_3D_DIRECTION_LEFT;
   } else if (x1 < x0 || x1 < x2) {
      GLfloat tmp;

      diffx02 = x0 - x2;
      diffy02 = y0 - y2;
      diffy12 = y1 - y2;

      delt02 = diffx02 / diffy02;
      tmp = x1 - (diffy12 * delt02 + x2);

      if (tmp <= 0.0)
         dwPrimitiveSet |= OP_3D_DIRECTION_LEFT;
   }
   
   tv0 = *v0;
   tv1 = *v1;
   tv2 = *v2;
   tv0.v.y = Y_FLIP(tv0.v.y);
   tv1.v.y = Y_FLIP(tv1.v.y);
   tv2.v.y = Y_FLIP(tv2.v.y);
   v0 = &tv0;
   v1 = &tv1;
   v2 = &tv2;
   
   y0 = v0->v.y;
   y1 = v1->v.y;
   y2 = v2->v.y;

/*   fprintf(stderr, "Vertex0 %f %f %f\n", v0->v.x, v0->v.y, v0->v.z);
   fprintf(stderr, "Vertex1 %f %f %f\n", v1->v.x, v1->v.y, v1->v.z);
   fprintf(stderr, "Vertex2 %f %f %f\n", v2->v.x, v2->v.y, v2->v.z);*/
   mWait3DCmdQueue(MMIO_VERT_REG_COUNT * 3 + 1);
   MMIO(REG_3D_PrimitiveSet, dwPrimitiveSet); 
   SIS_MMIO_WRITE_VERTEX(v0, 0, 0);
   SIS_MMIO_WRITE_VERTEX(v1, 1, 0);
   SIS_MMIO_WRITE_VERTEX(v2, 2, 1);
   mEndPrimitive();
}

static void TAG(sis6326_draw_line_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;
   sisVertexPtr v1 = (sisVertexPtr)(verts + smesa->vertex_size * 4);
   GLint dwPrimitiveSet = smesa->dwPrimitiveSet;

   if (abs(v0->v.y - v1->v.y) > abs(v0->v.x - v1->v.x))
   {
      dwPrimitiveSet |= OP_3D_DIRECTION_VERTICAL;
      if (v0->v.y > v1->v.y)
         dwPrimitiveSet |= OP_6326_3D_ATOP | OP_6326_3D_BBOT;
      else
         dwPrimitiveSet |= OP_6326_3D_BTOP | OP_6326_3D_ABOT;
   } else {
      if (v0->v.y > v1->v.y)
         dwPrimitiveSet |= OP_6326_3D_BTOP | OP_6326_3D_ABOT;
      else
         dwPrimitiveSet |= OP_6326_3D_ATOP | OP_6326_3D_BBOT;
   }

   mWait3DCmdQueue (MMIO_VERT_REG_COUNT * 2 + 1);
   MMIO(REG_3D_PrimitiveSet, dwPrimitiveSet); 
   SIS_MMIO_WRITE_VERTEX(v0, 0, 0);
   SIS_MMIO_WRITE_VERTEX(v1, 1, 1);
}

static void TAG(sis6326_draw_point_mmio)(sisContextPtr smesa, char *verts)
{
   sisVertexPtr v0 = (sisVertexPtr)verts;

   mWait3DCmdQueue (MMIO_VERT_REG_COUNT * 1 + 1);
   MMIO(REG_3D_PrimitiveSet, smesa->dwPrimitiveSet | OP_6326_3D_ATOP); 
   SIS_MMIO_WRITE_VERTEX(v0, 1, 1);
}
#endif

static INLINE void TAG(sis_vert_init)( void )
{
   sis_tri_func_mmio[SIS_STATES] = TAG(sis_draw_tri_mmio);
   sis_line_func_mmio[SIS_STATES] = TAG(sis_draw_line_mmio);
   sis_point_func_mmio[SIS_STATES] = TAG(sis_draw_point_mmio);
#if !(SIS_STATES & VERT_UV1)
   sis_tri_func_mmio[SIS_STATES | VERT_6326] = TAG(sis6326_draw_tri_mmio);
   sis_line_func_mmio[SIS_STATES | VERT_6326] = TAG(sis6326_draw_line_mmio);
   sis_point_func_mmio[SIS_STATES | VERT_6326] = TAG(sis6326_draw_point_mmio);
#endif
}

#undef TAG
#undef SIS_STATES