/******************************************************************************
 * ATI 3D RAGE SDK sample code                                                *
 *                                                                            *
 * R3ex2.c - RAGE PRO Example 2.                                              *
 *                                                                            *
 * Copyright (c) 1997 ATI Technologies Inc.  All rights reserved.             *
 ******************************************************************************/

#define NAME "RAGE PRO SDK Example 2"
#define TITLE "RAGE PRO ATI3DCIF Example 2"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>
#include <ddraw.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <direct.h>
#include "ati3dcif.h"
#include "exutil.h"
#include "glob.h"

#define CLAMPST_TEX  "..\\bmpetc\\brick.bmp"
#define CLAMPS_TEX   "..\\bmpetc\\mandrill.bmp"
#define CLAMPT_TEX   "..\\bmpetc\\brick.bmp"
#define NOCLAMP_TEX  "..\\bmpetc\\mandrill.bmp"

void CloseApp (void);
void DrawFrame (void);
void UpdateFrameInfo (void);

// Texture structure for texture information.

TEXTURE gTex1 = {0};
TEXTURE gTex2 = {0};
TEXTURE gTex3 = {0};
TEXTURE gTex4 = {0};

// Globals.

HWND    ghWnd;
BOOL gbDisplayFrameRate = FALSE;
BOOL gbDrawFrame = FALSE;
BOOL gbTMap = FALSE;
BOOL gbCompTexEn = FALSE;
DWORD gdwFillColor = 0x0000000fl;


// colored vertices mag for 128x128
C3D_VTCF vtcfPrim1TriList[6]= {
    {  80.0f,  10.0f, 0.0f, 0.0f, 1.5f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f,  10.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f},   
    {  80.0f, 200.0f+10.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f, 200.0f+10.0f, 0.0f, 1.5f, 0.0f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}, 
    {  80.0f, 200.0f+10.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f,  10.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}   
};

// colored vertices min for 128x128 tex
C3D_VTCF vtcfPrim2TriList[6]= {
    { 320.0f+40.0f, 10.0f, 0.0f, 0.0f, 1.5f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f,  10.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f},   
    { 320.0f+40.0f, 10.0f+200.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f, 10.0f+200.0f, 0.0f, 1.5f, 0.0f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}, 
    { 320.0f+40.0f, 10.0f+200.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f,  10.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}   
};

C3D_VTCF vtcfPrim3TriList[6]= {
    {  80.0f,  250.0f, 0.0f, 0.0f, 1.5f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f,  250.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f},   
    {  80.0f, 200.0f+250.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f, 200.0f+250.0f, 0.0f, 1.5f, 0.0f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}, 
    {  80.0f, 200.0f+250.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 200.0f+80.0f,  250.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}   
};

// colored vertices min for 128x128 tex
C3D_VTCF vtcfPrim4TriList[6]= {
    { 320.0f+40.0f, 250.0f, 0.0f, 0.0f, 1.5f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f,  250.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f},   
    { 320.0f+40.0f, 250.0f+200.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f, 250.0f+200.0f, 0.0f, 1.5f, 0.0f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}, 
    { 320.0f+40.0f, 250.0f+200.0f, 0.0f, 0.0f, 0.0f, 1.0f, 255.0f, 255.0f, 255.0f, 0.0f}, 
    { 320.0f+40.0f+200.0f,  250.0f, 0.0f, 1.5f, 1.5f, 1.0f,   0.0f,   0.0f,   0.0f, 0.0f}   
};

// Primitive list array of vertex pointers.

C3D_PVTCF vlstPrim1List[6];
C3D_PVTCF vlstPrim2List[6];
C3D_PVTCF vlstPrim3List[6];
C3D_PVTCF vlstPrim4List[6];

// Clamp setting strings.

char *gpszClampMode [] = {
    "Clamp S, Clamp T",
    "Clamp S",
    "Clamp T",
    "No Clamping"
};

/******************************************************************************
 * WindowProc                                                                 *
 *  Function: main window message handling procedure                          *
 *    Inputs: hWnd - handle of window                                         *
 *            message - message to window                                     *
 *            wParam - WORD length parameter for message (varies)             *
 *            lParam - DWORD length parameter for message (varies)            *
 *   Outputs: varies                                                          *
 ******************************************************************************/

long FAR PASCAL WindowProc (HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
        case WM_CREATE:
            break;

        case WM_SETCURSOR:
            SetCursor (NULL);
            return TRUE;

        case WM_KEYDOWN:
            switch (wParam)
            {
                case VK_ESCAPE:
                    DestroyWindow (hWnd);
                    return 0;

            } // switch
            break;

        case WM_SYSKEYDOWN:
            switch (wParam)
            {
                case 'F':
                case 'f':

                    // Toggle frame rate counter state.

                    gbDisplayFrameRate = !gbDisplayFrameRate;
                    if (gbDisplayFrameRate)
                    {
                        StartFrameCounter ();
                    }
                    else
                    {
                        EndFrameCounter ();
                    } // if
                    break;
            } // switch
            break;

        case WM_DESTROY:
            gbDrawFrame = FALSE;
            CloseApp ();
            PostQuitMessage (0);
            break;
    } // switch

    return DefWindowProc (hWnd, message, wParam, lParam);

} // WindowProc


/******************************************************************************
 * InitApp                                                                    *
 *  Function: do work required for every instance of the application:         *
 *            create the window, initialize data                              *
 *    Inputs: hInstance - instance handle of application                      *
 *            nCmdShow - current visibility state                             *
 *   Outputs: TRUE - initialization was successful                            *
 *            FALSE - initialization failed                                   *
 ******************************************************************************/

static BOOL InitApp (HANDLE hInstance, int nCmdShow)
{
    HWND hwnd;
    WNDCLASS wc;
    int i;
    char cExePath[_MAX_PATH], cExeDir[_MAX_DIR];

    // Make sure we are in the right directory so relative paths will behave.

    GetModuleFileName (hInstance, cExePath, _MAX_PATH);
    _splitpath (cExePath, NULL, cExeDir, NULL, NULL);
    _chdir (cExeDir);

    // Set up and register window class.

    wc.style = CS_HREDRAW | CS_VREDRAW;
    wc.lpfnWndProc = WindowProc;
    wc.cbClsExtra = 0;
    wc.cbWndExtra = 0;
    wc.hInstance = hInstance;
    wc.hIcon = LoadIcon (hInstance, IDI_APPLICATION);
    wc.hCursor = LoadCursor (NULL, IDC_ARROW);
    wc.hbrBackground = NULL;
    wc.lpszMenuName = NAME;
    wc.lpszClassName = NAME;
    RegisterClass (&wc);

    // Create a window.

    ghWnd = hwnd = CreateWindowEx (WS_EX_TOPMOST, NAME, TITLE, WS_POPUP, 0, 0,
                                   GetSystemMetrics (SM_CXSCREEN),
                                   GetSystemMetrics (SM_CYSCREEN), NULL, NULL,
                                   hInstance, NULL);

    if (!hwnd)
    {
        return FALSE;
    } // if

    ShowWindow (hwnd, nCmdShow);
    UpdateWindow (hwnd);

    // Create and initialize the main DirectDraw object.

    if (!InitDirectDraw (hwnd, 640, 480, 16))
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Create an ATI CIF rendering context.

    if (!InitATI3DCIF ())
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Load clamp s, clamp t texture.

    if (!LoadTexture (CLAMPST_TEX, &gTex1, TMAP_CLAMPS | TMAP_CLAMPT))
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Load clamp s texture.

    if (!LoadTexture (CLAMPS_TEX, &gTex2, TMAP_CLAMPS))
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Load clamp t texture.

    if (!LoadTexture (CLAMPT_TEX, &gTex3, TMAP_CLAMPT))
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Load unclamped texture.

    if (!LoadTexture (NOCLAMP_TEX, &gTex4, 0))
    {
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Enable the texture.

    gbTMap = TRUE;
    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_EN, &gbTMap) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not enable texture map");
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Initialize the primitive lists.

    for (i = 0 ; i < 6; i++)
    {
        vlstPrim1List[i] = &(vtcfPrim1TriList[i]);
        vlstPrim2List[i] = &(vtcfPrim2TriList[i]);
        vlstPrim3List[i] = &(vtcfPrim3TriList[i]);
        vlstPrim4List[i] = &(vtcfPrim4TriList[i]);
    } // for

    // Start the frame counter.

    StartFrameCounter ();

    // Enable drawing flag.

    gbDrawFrame = TRUE;

    return TRUE;

} // InitApp


/******************************************************************************
 * WinMain                                                                    *
 *  Function: initialization, message loop                                    *
 *    Inputs: hInstance - handle for this instance of application             *
 *            hPrevInstance - handle for previous instance of application     *
 *            lpCmdLine - string containing remainder of commmand line        *
 *            nCmdShow - current visibility state                             *
 *   Outputs: varies                                                          *
 ******************************************************************************/

int PASCAL WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
                    LPSTR lpCmdLine, int nCmdShow)
{
    MSG     msg;
    float   fDelayCounter = 0.0f;

    lpCmdLine = lpCmdLine;
    hPrevInstance = hPrevInstance;

    if (!InitApp (hInstance, nCmdShow))
    {
        return FALSE;
    } // if

    while (TRUE)
    {
        if (PeekMessage (&msg, NULL, 0, 0, PM_NOREMOVE))
        {
            if (!GetMessage (&msg, NULL, 0, 0))
            {
                return msg.wParam;
            } // if
            TranslateMessage (&msg);
            DispatchMessage (&msg);
        } // if

        // Update object geomtery.

        if (gbDrawFrame)
        {
            DrawFrame ();

            UpdateFrameInfo ();

        } // if
    } // while

    return msg.wParam;

} // WinMain


/******************************************************************************
 * CloseApp                                                                   *
 *  Function: unregister texture, close ATI 3D module, and destroy DirectDraw *
 *            object                                                          *
 *    Inputs: none                                                            *
 *   Outputs: none                                                            *
 ******************************************************************************/

void CloseApp (void)
{
    // Stop frame counter.

    EndFrameCounter ();

    // Unload the clamp s, clamp t texture.

    if (gTex1.lpDDSTex || gTex1.hTX)
    {
        if (!UnloadTexture (&gTex1))
        {
            MessageBox (ghWnd, gszErrMsg, TITLE, MB_OK);
        } // if
    } // if

    // Unload the clamp s texture.

    if (gTex2.lpDDSTex || gTex2.hTX)
    {
        if (!UnloadTexture (&gTex2))
        {
            MessageBox (ghWnd, gszErrMsg, TITLE, MB_OK);
        } // if
    } // if

    // Unload the clamp t texture.

    if (gTex3.lpDDSTex || gTex3.hTX)
    {
        if (!UnloadTexture (&gTex3))
        {
            MessageBox (ghWnd, gszErrMsg, TITLE, MB_OK);
        } // if
    } // if

    // Unload the unclamped texture.

    if (gTex4.lpDDSTex || gTex4.hTX)
    {
        if (!UnloadTexture (&gTex4))
        {
            MessageBox (ghWnd, gszErrMsg, TITLE, MB_OK);
        } // if
    } // if

    // Release surface.

    if (glpDDSOne)
    {
        glpDDSOne->lpVtbl->Release (glpDDSOne);
        glpDDSOne = NULL;
    } // if

    // Destroy the ATI 3D rendering context and close the 3D Driver.

    CloseATI3DCIF ();

    // Destroy the DirectDraw object.

    CloseDirectDraw ();

} // CloseApp


/******************************************************************************
 * DrawFrame                                                                  *
 *  Function: draw the animation frame and flip the double buffers            *
 *    Inputs: none                                                            *
 *   Outputs: none                                                            *
 ******************************************************************************/

void DrawFrame (void)
{
    HRESULT ddrval;
    C3D_EC ecRend;
    DDSURFACEDESC ddsd;
    DDBLTFX ddbltfx;
    HDC hdc;
    TEXTMETRIC tm;
    int ytextoffset = 0;
    int ytextoffsetdelta;

    //  Color fill the back surface.

    ZeroMemory (&ddbltfx, sizeof (ddbltfx));
    ddbltfx.dwSize = sizeof (ddbltfx);
    ddbltfx.dwFillColor = gdwFillColor;
    glpDDSBack->lpVtbl->Blt (glpDDSBack, NULL, NULL, NULL, DDBLT_COLORFILL | 
                             DDBLT_WAIT, &ddbltfx);

    // Lock the back surface to get surface memory address.

    ZeroMemory (&ddsd, sizeof (ddsd));
    ddsd.dwSize = sizeof (ddsd);
    ddrval = glpDDSBack->lpVtbl->Lock (glpDDSBack, NULL, &ddsd,
                                       DDLOCK_SURFACEMEMORYPTR | DDLOCK_WAIT, NULL);
    if (ddrval == DDERR_SURFACELOST) glpDDSBack->lpVtbl->Restore (glpDDSBack);

    // Unlock the surface.

    ddrval = glpDDSBack->lpVtbl->Unlock (glpDDSBack, NULL);

    // Switch to 3D mode.

    if ((ecRend = ATI3DCIF_RenderBegin (ghRC)) != C3D_EC_OK) return;

    // Set the pointer to the frame buffer address of the back surface.

    ATI3DCIF_ContextSetState (ghRC, C3D_ERS_SURF_DRAW_PTR,
                              (C3D_PRSDATA) &(ddsd.lpSurface));

    // Draw a tristrip into the selected buffer.

    // Select the clamp t, clamp s texture .

    ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex1.hTX);

    // Draw the top left square.

    ecRend = ATI3DCIF_RenderPrimList ((C3D_VSTRIP) vlstPrim1List, 6);

    // Select the clamp s texture .

    ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex2.hTX);

    // Draw the top right square.

    ecRend = ATI3DCIF_RenderPrimList ((C3D_VSTRIP) vlstPrim2List, 6);

    // Select the clamp t texture .

    ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex3.hTX);

    // Draw the bottom left square.

    ecRend = ATI3DCIF_RenderPrimList ((C3D_VSTRIP) vlstPrim3List, 6);

    // Select the unclamped texture .

    ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex4.hTX);

    // Draw the bottom right square.

    ecRend = ATI3DCIF_RenderPrimList ((C3D_VSTRIP) vlstPrim4List, 6);

    // Switch back to 2D mode.

    ecRend = ATI3DCIF_RenderEnd ();

    // Display strings.

    if (glpDDSBack->lpVtbl->GetDC (glpDDSBack, &hdc) == DD_OK)
    {
        // Set backgroun and text colors.

        SetBkColor (hdc, RGB (0, 0, 0x1f));
        SetTextColor (hdc, RGB (0xff, 0xff, 0xff));

        // Get text metric information.

        GetTextMetrics (hdc, &tm);

        // Write output strings.

        ytextoffsetdelta = (int) tm.tmAscent + (int) tm.tmInternalLeading;
        ytextoffset = 200 + ytextoffsetdelta; 
        TextOut (hdc, 80, ytextoffset, gpszClampMode[0], lstrlen (gpszClampMode[0]));
        TextOut (hdc, 320+40, ytextoffset, gpszClampMode[1], lstrlen (gpszClampMode[2]));
        ytextoffset += 240; 
        TextOut (hdc, 80, ytextoffset, gpszClampMode[2], lstrlen (gpszClampMode[2]));
        TextOut (hdc, 320+40, ytextoffset, gpszClampMode[3], lstrlen (gpszClampMode[3]));

        // Release DC.

        glpDDSBack->lpVtbl->ReleaseDC (glpDDSBack, hdc);
    } // if

    // Update the frame rate display if enabled.

    if (gbDisplayFrameRate) DisplayFrameCounter (glpDDSBack);

    // Flip the primary and back surfaces.

    PageFlip (glpDDSPrimary);

} // DrawFrame


/******************************************************************************
 * UpdateFrameInfo                                                            *
 *  Function: update the texture coordinates.                                 *
 *    Inputs: none                                                            *
 *   Outputs: none                                                            *
 ******************************************************************************/

void UpdateFrameInfo (void)
{
    static float st = 1.5f;
    static float delta_st = 0.01f;

    st+=delta_st;
    if ((st > 1.9f) || (st < 0.9f))
    {
        delta_st = -delta_st;
    }

    vtcfPrim1TriList[0].t = st;
    vtcfPrim1TriList[1].s = st;
    vtcfPrim1TriList[1].t = st;
    vtcfPrim1TriList[3].s = st;
    vtcfPrim1TriList[5].s = st;
    vtcfPrim1TriList[5].t = st;

    vtcfPrim2TriList[0].t = st;
    vtcfPrim2TriList[1].s = st;
    vtcfPrim2TriList[1].t = st;
    vtcfPrim2TriList[3].s = st;
    vtcfPrim2TriList[5].s = st;
    vtcfPrim2TriList[5].t = st;

    vtcfPrim3TriList[0].t = st;
    vtcfPrim3TriList[1].s = st;
    vtcfPrim3TriList[1].t = st;
    vtcfPrim3TriList[3].s = st;
    vtcfPrim3TriList[5].s = st;
    vtcfPrim3TriList[5].t = st;

    vtcfPrim4TriList[0].t = st;
    vtcfPrim4TriList[1].s = st;
    vtcfPrim4TriList[1].t = st;
    vtcfPrim4TriList[3].s = st;
    vtcfPrim4TriList[5].s = st;
    vtcfPrim4TriList[5].t = st;
}
