/****************************************************************************
 * RAGE128 Chapter 6 Sample Code                                            *
 *                                                                          *
 * rendst.c - This program demonstrates how to set dithering, shading, and  *
 * culling states using CCE command packets.                                *
 *                                                                          *
 * Copyright (c) 1999 ATI Technologies Inc.  All rights reserved.           *
 ****************************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <i86.h>
#include <conio.h>
#include "..\..\util\regdef.h"
#include "..\..\util\defines.h"
#include "..\..\util\main.h"
#include "..\..\util\cce.h"
#include "..\util\cntx3d.h"
#include "..\util\prim.h"
#include "..\util\rendstat.h"
#include "..\util\util3d.h"

char gErrMsg [64] = {0};

extern DWORD BPPOverride; // Override default 8bpp mode in Chap 3 lib.


/****************************************************************************
 * main                                                                     *
 *  Function: Main loop.                                                    *
 *    Inputs: none                                                          *
 *   Outputs: none                                                          *
 ****************************************************************************/

void main (int argc, char *argv[])
{
	int i, exitcode = 0;
	int ch, exitwhile = 0;
    DWORD DitherEn = 0;
    ESHADEMODE ShadeMode = SHADE_GOURAUD;
    EFLATSHADEVERTEX FlatShadeVertex = FLATSHADEVERTEX_D3D;
    EFRONTDIR FrontDir = FRONTDIR_CW;
    ECULLFCN FrontCullFcn = CULLFCN_DRAW_REV_SOLID;
	ECULLFCN BackCullFcn = CULLFCN_CULL;

    printf ("Rage 128 Sample Code, Chapter 6.\n"
        "RENDST.EXE\n\n"
        "This program demonstrates how to use Rage 128 CCE command packets\n"
        "to set culling, dithering, and shading render states.\n\n"
        "Mode resolution (eg 640 480) and colour depth (15, 16, 32)\n"
        "should be passed as command line arguments.\n"
        "Default resolution and colour depth is 640x480, 32bpp. Run this\n"
        "program in a 16bpp display mode to view the effects of dithering.\n\n"
        "The following keys may be used:\n"
        "\tD: toggle dithering on and off.\n"
        "\tS: cycle through shading modes.\n"
        "\tV: switch between D3D and OpenGL flat shading convention:\n"
        "\t   D3D-color of 1st vertex, OpenGL-color of 3rd vertex.\n"
        "\tC: switch between CW and CCW front facing directions.\n"
        "\tF: cycle through front facing culling functions.\n"
        "\tB: cycle through back facing culling functions.\n"
        "\tESC: exit the program.\n\n"
        "Press any key to continue.\n\n");

    getch ();

    // Inspect arguments for invalid color depth.

    for (i = 1; i < argc; i++)
    {
        if ((strcmp (argv[i], "8") == 0) || (strcmp (argv[i], "24") == 0))
		{
			printf ("\nThis program does not operate at 8 or 24 bpp.");
			printf ("\nProgram terminated.");
			exit (1);
		}
	}

    // Override default 8bpp mode in Chap 3 lib.

    BPPOverride = 16;

    // First, run StartUp function to set up the application

    R128_StartUp (argc, argv);

    // Clear the screen

    R128_ClearScreen (WHITE);

    // Initialize the CCE microengine.

    if (R128_CCEInit (CCE_MODE_192BM) != CCE_SUCCESS)
    {
        R128_ShutDown ();
        printf ("R128_CCEInit failed!!\n");
        exit (1);
    } // if

	// Set the default 3D context.

    R128_Init3DContext ();

	// Set dither enable mode.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_DITHERMODE, (DitherEn & 1)),
		"dither enable failed!!")

	// Set the shading mode.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_SHADEMODE, (DWORD)ShadeMode),
		"set shade mode failed!!")

	// Set the flat shade coloring vertex.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_FLATSHADEVERTEX, 
		(DWORD)FlatShadeVertex), "set flat-shade vertex failed!!")

	// Set triangle front direction.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_FRONTDIR, (DWORD)FrontDir),
		"set front direction failed!!")

	// Set front face culling function.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_FRONTFACE_CULL_FCN, 
		(DWORD)FrontCullFcn),
		"set front-face culling function failed!!")

	// Set back face culling function.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_BACKFACE_CULL_FCN, 
		(DWORD)BackCullFcn),
		"set back-face culling function failed!!")

	// Set solid-color.

    ATTEMPT (R128_SetRenderState (RENDERSTATE_SOLIDCOLOR, 0x00ff00ff),
		"set solid color failed!!")

	// Loop processing keyress input.

	while (exitwhile == 0)
	{
        // Clear the screen.

        R128_CCEClearScreen (0x00ffffff);

        // Draw a single triangle.

		DrawTriangle ();

		// Get keypress input.

        while (!kbhit());

        ch = getch ();

		switch (ch)
		{
		case 27: // ESC
			exitwhile = 1;
			break;

        case 'd':
        case 'D':
            DitherEn++;

            R128_SetRenderState (RENDERSTATE_DITHERMODE,
                (DitherEn & 1));
			break;

        case 's':
        case 'S':
            ShadeMode++;
            if (ShadeMode > SHADE_GOURAUD)
                ShadeMode = SHADE_SOLIDCOLOR;

            R128_SetRenderState (RENDERSTATE_SHADEMODE,
                (DWORD)ShadeMode);
			break;

        case 'v':
        case 'V':
            FlatShadeVertex++;
            if (FlatShadeVertex > FLATSHADEVERTEX_OPENGL)
                FlatShadeVertex = FLATSHADEVERTEX_D3D;

            R128_SetRenderState (RENDERSTATE_FLATSHADEVERTEX,
                (DWORD)FlatShadeVertex);
			break;

		case 'c':
		case 'C':
            FrontDir++;
            if (FrontDir > FRONTDIR_CCW)
                FrontDir = FRONTDIR_CW;

            R128_SetRenderState (RENDERSTATE_FRONTDIR,
                (DWORD)FrontDir);
			break;

        case 'f':
        case 'F':
			FrontCullFcn++;
            if (FrontCullFcn > CULLFCN_DRAW_REV_SOLID)
				FrontCullFcn = CULLFCN_CULL;

            R128_SetRenderState (RENDERSTATE_FRONTFACE_CULL_FCN,
                (DWORD)FrontCullFcn);
			break;

        case 'b':
        case 'B':
			BackCullFcn++;
            if (BackCullFcn > CULLFCN_DRAW_REV_SOLID)
				BackCullFcn = CULLFCN_CULL;

            R128_SetRenderState (RENDERSTATE_BACKFACE_CULL_FCN,
                (DWORD)BackCullFcn);
			break;

		} // switch
	} // while 

exit_app:

	// Close 3D context, shut down micro-engine and restore original mode.

	R128_Close3DContext ();
    R128_CCEEnd (CCE_END_WAIT);
    R128_ShutDown ();

	// print error message if any.

	if (exitcode == 1)
		printf ("%s\n", gErrMsg);

	exit (exitcode);
} // main


/****************************************************************************
 * DrawTriangle                                                             *
 *  Function: Draw a single gouraud shaded triangle.                        *
 *    Inputs: none                                                          *
 *   Outputs: none                                                          *
 ****************************************************************************/

void DrawTriangle (void)
{
    TLVERTEX2 triverts[3];

    // Set the triangle vertices:

    // Vertex 0:

    triverts[0].x = ((float)R128_AdapterInfo.xres/2.0f);
    triverts[0].y = ((float)R128_AdapterInfo.yres/4.0f);
    triverts[0].z = 0.5f;
    triverts[0].rhw = 1.0f;
    triverts[0].diffuse = 0x000000ff;
    triverts[0].specular = 0x00000000;
    triverts[0].s1 = 0.5f;
    triverts[0].t1 = 1.0f;
    triverts[0].s2 = 0.5f;                                                                  // s2
    triverts[0].t2 = 0.0f;                                                                  // t2

    // Vertex 1:

    triverts[1].x = (float)R128_AdapterInfo.xres * 0.75f;
    triverts[1].y = (float)R128_AdapterInfo.yres * 0.75f;
    triverts[1].z = 0.5f;
    triverts[1].rhw = 1.0f;
    triverts[1].diffuse = 0x0000ff00;
    triverts[1].specular = 0x00000000;
    triverts[1].s1 = 1.0f;
    triverts[1].t1 = 0.0f;
    triverts[1].s2 = 1.0f;
    triverts[1].t2 = 1.0f;

    // Vertex 2:

    triverts[2].x = (float)R128_AdapterInfo.xres * 0.25f;
    triverts[2].y = (float)R128_AdapterInfo.yres * 0.75f;
    triverts[2].z = 0.5f;
    triverts[2].rhw = 1.0f;
    triverts[2].diffuse = 0x00ff0000;
    triverts[2].specular = 0x00000000;
    triverts[2].s1 = 0.0f;
    triverts[2].t1 = 0.0f;
    triverts[2].s2 = 0.0f;
    triverts[2].t2 = 1.0f;

    // Draw a single triangle.

    R128_DrawTriangle (triverts);

}

