/*
** Copyright (c) 1997, 3Dfx Interactive, Inc.
** All Rights Reserved.
**
** This is UNPUBLISHED PROPRIETARY SOURCE CODE of 3Dfx Interactive, Inc.;
** the contents of this file may not be disclosed to third parties, copied or
** duplicated in any form, in whole or in part, without the prior written
** permission of 3Dfx Interactive, Inc.
**
** RESTRICTED RIGHTS LEGEND:
** Use, duplication or disclosure by the Government is subject to restrictions
** as set forth in subdivision (c)(1)(ii) of the Rights in Technical Data
** and Computer Software clause at DFARS 252.227-7013, and/or in similar or
** successor clauses in the FAR, DOD or NASA FAR Supplement. Unpublished -
** rights reserved under the Copyright Laws of the United States.
**
*/

#include <assert.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <glide.h>
#include "tlib.h"


GrHwConfiguration hwConfig;
static char version[80];

static const char name[]    = "test04";
static const char purpose[] = "draws gouraud shaded triangle";
static const char usage[]   = "-a <anti-aliased triangle> -n <frames> -r <res> -d <filename>";

static void
XLateTri(GrVertex* a, GrVertex* b, GrVertex* c,
         const float theta, const float scale,
         const float x, const float y, const float z);

void main(int argc, char **argv) {
  char match; 
  char **remArgs;
  int  rv;

  GrScreenResolution_t resolution = GR_RESOLUTION_640x480;
  float                scrWidth   = 640.0f;
  float                scrHeight  = 480.0f;
  int frames                      = -1;
  FxBool               scrgrab    = FXFALSE;
  FxBool               useAA      = FXFALSE;
  char                 filename[256];
  int i;

  /* Process Command Line Arguments */
  while(rv = tlGetOpt(argc, argv, "anrd", &match, &remArgs)) {
    if (rv == -1) {
      printf("Unrecognized command line argument\n");
      printf("%s %s\n", name, usage);
      printf("Available resolutions:\n%s\n",
             tlGetResolutionList());
      return;
    }
    switch(match) {
    case 'a':
      useAA = FXTRUE;
      break;

    case 'n':
      frames = atoi(remArgs[0]);
      break;

    case 'r':
      resolution = tlGetResolutionConstant(remArgs[0], 
                                           &scrWidth, 
                                           &scrHeight);
      break;
    case 'd':
      scrgrab = FXTRUE;
      frames = 1;
      strcpy(filename, remArgs[0]);
      break;
    }
  }

  tlSetScreen(scrWidth, scrHeight);

  grGlideGetVersion(version);

  printf("%s:\n%s\n", name, purpose);
  printf("%s\n", version);
  printf("Resolution: %s\n", tlGetResolutionString(resolution));
  if (frames == -1) {
    printf("Press A Key To Begin Test.\n");
    tlGetCH();
  }
    
  /* Initialize Glide */
  grGlideInit();
  assert(grSstQueryHardware(&hwConfig));

  for(i = 0; i < hwConfig.num_sst; i++) {
    grSstSelect(i);
    assert(grSstWinOpen(0,
                        resolution,
                        GR_REFRESH_60Hz,
                        GR_COLORFORMAT_ABGR,
                        GR_ORIGIN_UPPER_LEFT,
                        2, 1));

    /* Set up Render State - gouraud shading */
    grColorCombine(GR_COMBINE_FUNCTION_LOCAL,
                   GR_COMBINE_FACTOR_NONE,
                   GR_COMBINE_LOCAL_ITERATED,
                   GR_COMBINE_OTHER_NONE,
                   FXFALSE);
  }

  /* Make sure we select the primary before setting up the conole just
   * in case it has different texture parameters.  
   */
  grSstSelect(0);
  tlConSet(0.0f, 0.0f, 1.0f, 1.0f, 
           60, 30, 0xffffff);
    
  tlConOutput("Press a key to quit\n");
  while(frames-- && tlOkToRender()) {
    static float x = 0.0f;
    static float rot = 0.0f;
    const float triEdge = MIN(scrWidth, scrHeight) / 8.0f;

    for(i = 0; i < hwConfig.num_sst; i++) {
      grSstSelect(i);
      grBufferClear(0x00, 0, GR_WDEPTHVALUE_FARTHEST);

      if (hwConfig.SSTs[i].type == GR_SSTTYPE_SST96) {
        tlGetDimsByConst(resolution, &scrWidth, &scrHeight);
        grClipWindow(0, 0, (FxU32)scrWidth, (FxU32)scrHeight);
      }

      {
        const float sin60 = 0.8660254f;
        const float baseXCoor = (triEdge / 2.0f);
        const float baseYCoor = ((sin60 * triEdge) / 2.0f);
        GrVertex templateA = {
          0.0f,   /* x: - (triEdge / 2.0f) */
          0.0f,   /* y: - ((sin60 * triEdge) / 2.0f) */
          0.0f,   /* z */
          255.0f, /* r */
          0.0f,   /* g */
          0.0f    /* b */
        };
        GrVertex templateB = {
          0.0f,   /* x: (triEdge / 2.0f) */
          0.0f,   /* y: - ((sin60 * triEdge) / 2.0f) */
          0.0f,   /* z */
          0.0f,   /* r */
          255.0f, /* g */
          0.0f    /* b */
        };
        GrVertex templateC = {
          0.0f,  /* x: 0.0f */
          0.0f,  /* y: ((sin60 * triEdge) / 2.0f) */
          0.0f,  /* z */
          0.0f,  /* r */
          0.0f,  /* g */
          255.0f /* b */
        };

        templateA.x = -baseXCoor;
        templateA.y = -baseYCoor;

        templateB.x = baseXCoor;
        templateB.y = -baseYCoor;

        templateC.x = 0.0f;
        templateC.y = baseYCoor;
        
        XLateTri(&templateA, &templateB, &templateC, 
                 -1.0f * rot, 2.0f,
                 x - (i * scrWidth), (scrHeight / 2.0f), 0.0f);

        /* Clamp the triangle to the screen space of the current board */
        if (((templateA.x >= -triEdge) && (templateA.x <= scrWidth + triEdge)) &&
            ((templateB.x >= -triEdge) && (templateB.x <= scrWidth + triEdge)) &&
            ((templateC.x >= -triEdge) && (templateC.x <= scrWidth + triEdge))) {
          if (useAA) 
            grAADrawTriangle(&templateA, &templateB, &templateC,
                             FXTRUE, FXTRUE, FXTRUE);
          else 
            grDrawTriangle(&templateA, &templateB, &templateC);
        }
      }

      if (i == 0) tlConRender();
      grBufferSwap(1);
    }

    /* Advance triangle position */
    x += 1.0f;
    if (x > ((hwConfig.num_sst * scrWidth) + triEdge)) x = 0.0f;
        
    /* Advance triangle rotation */
#define kPi 3.14159f
    rot += (kPi / 180.0f);
    if (rot >= (2.0f * kPi)) rot = 0.0f;

    /* grab the frame buffer */
    if (scrgrab) {
      if (!tlScreenDump(filename, (FxU16)scrWidth, (FxU16)scrHeight))
        printf("Cannot open %s\n", filename);
      scrgrab = FXFALSE;
    }

    if (tlKbHit()) frames = 0;
  }
    
  grGlideShutdown();
}

static void
XLateTri(GrVertex* a, GrVertex* b, GrVertex* c,
         const float theta, const float scale,
         const float x, const float y, const float z)
{
  GrVertex* vertList[3];
  const float cosTheta = (const float)cos(theta);
  const float sinTheta = (const float)sin(theta);
  int i;

  vertList[0] = a;
  vertList[1] = b;
  vertList[2] = c;

  for(i = 0; i < sizeof(vertList) / sizeof(vertList[0]); i++) {
    const GrVertex* curVertex = vertList[i];
    GrVertex tempVert = *curVertex;

    tempVert.x = ((scale * cosTheta * curVertex->x) +
                  (-sinTheta * curVertex->y) +
                  (0.0f * curVertex->z) +
                  (x * 1.0f));

    tempVert.y = ((sinTheta * curVertex->x) +
                  (scale * cosTheta * curVertex->y) +
                  (0.0f * curVertex->z) +
                  (y * 1.0f));

    tempVert.z = ((0.0f * curVertex->x) +
                  (0.0f * curVertex->y) +
                  (scale * 1.0f * curVertex->z) +
                  (0.0f * 1.0f));
    tempVert.ooz = (1.0f * tempVert.z);

    *vertList[i] = tempVert;
  }
}
