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

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

#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 PRIMARY_TEX   "..\\bmpetc\\brick.bmp"
#define SECONDARY_TEX   "..\\bmpetc\\mandrill.bmp"
//#define PRIMARY_TEX   "..\\bmpetc\\teds\\texture5.bmp"
//#define SECONDARY_TEX "..\\bmpetc\\teds\\texture4.bmp"

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

// Texture structure for texture information.

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

// Globals

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


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

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

// Primitive list array of vertex pointers.

C3D_PVTCF vlstPrim1List[6];
C3D_PVTCF vlstPrim2List[6];

// Texture compositing function strings.

char *lpszTexCompFnc[] = {
    "Texture composite function [F1]: Blend",
    "Texture composite function [F1]: Modulate",
    "Texture composite function [F1]: Add Specular",
    "undefined"
};

// Texture compositing factor string.

char gszCompositeFactor [128] = "";

/******************************************************************************
 * 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;

                case VK_F1:
                    // Disable screen drawing while changing the texture compositing function.

                    gbDrawFrame = FALSE;

                    // Increment texture compositing function selector.

                    gu32TexCompFnc++;
                    if ((gu32TexCompFnc == (C3D_UINT32)C3D_ETEXCOMPFCN_MAX)
                        || (gu32TexCompFnc == (C3D_UINT32)C3D_ETEXCOMPFCN_ADD_SPEC)) // temporary disable: not implemented yet.
                    {
                        gu32TexCompFnc = (C3D_UINT32)C3D_ETEXCOMPFCN_BLEND;
                    }

                    // Set texture compositing function.

                    ATI3DCIF_ContextSetState ( ghRC, C3D_ERS_COMPOSITE_FNC, &gu32TexCompFnc );

                    // Re-enable screen drawing.

                    gbDrawFrame = TRUE;
                    break;

            } // 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];
    C3D_ETEXFILTER eFilter;
    C3D_UINT32 u32BlendFactor = 8;

    // 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 BRICK.BMP texture.

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

    // Load MANDRILL.BMP texture.

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

    // Select the texture.

    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_TMAP_SELECT, &gTex1.hTX) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not select texture map");
        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

    // Let's enable composite texturing

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

    // Let's select our secondary composite texture texture. Texture selected by
    // C3D_ERS_TMAP_SELECT, &gTex1.hTX is our primary. 

    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_SELECT, &gTex2.hTX) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not select secendary composite texture map");
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Set the texture compositing function to BLEND.

    gu32TexCompFnc = (C3D_UINT32)C3D_ETEXCOMPFCN_BLEND;
    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_FNC, &gu32TexCompFnc) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not select texture compositing function");
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Initialize the blend factor to eight (mid-point value).

    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_FACTOR, &u32BlendFactor) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not set blending factor");
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Update texture compositing factor string.

    sprintf (gszCompositeFactor, "Texture Composite Factor = %u", u32BlendFactor);

    // Set texture filering mode.

    eFilter = C3D_ETFILT_MIN2BY2_MAG2BY2;
    if (ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_FILTER, &eFilter) != C3D_EC_OK)
    {
        wsprintf (gszErrMsg, "Could not set composite filter mode");
        CloseApp ();
        MessageBox (hwnd, gszErrMsg, TITLE, MB_OK);
        DestroyWindow (hwnd);
        return FALSE;
    } // if

    // Initialize the primitive list.

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

    // Start frame counter.

    StartFrameCounter ();

    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 ();

            if (gu32TexCompFnc == (C3D_UINT32)C3D_ETEXCOMPFCN_BLEND)
                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 texture.

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

    // Unload the texture.

    if (gTex2.lpDDSTex || gTex2.hTX)
    {
        if (!UnloadTexture (&gTex2))
        {
            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 the magnified triangle list.

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

    // Draw the minified triangle list.

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

    // Switch back to 2D mode.

    ecRend = ATI3DCIF_RenderEnd ();

    // Display texture composite function string.

    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 = 320 + ytextoffsetdelta; 
        TextOut (hdc, 10, ytextoffset, lpszTexCompFnc[gu32TexCompFnc], lstrlen (lpszTexCompFnc[gu32TexCompFnc]));

        // Write texture compositing factor string if texture compositing function = blend.

        if (gu32TexCompFnc == (C3D_UINT32)C3D_ETEXCOMPFCN_BLEND)
        {
            ytextoffset += ytextoffsetdelta; 
            TextOut (hdc, 10, ytextoffset, gszCompositeFactor, lstrlen (gszCompositeFactor));
        }

        // 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 compositing factor.                          *
 *    Inputs: none                                                            *
 *   Outputs: none                                                            *
 ******************************************************************************/

void UpdateFrameInfo (void)
{
    static BOOL bFactorIncreasing = TRUE;
    static DWORD dwCompositeFactor = 8;

    // Increase or decrease texture compositing factor.

    if (bFactorIncreasing)
    {
        if (dwCompositeFactor >= 15) 
        {
            dwCompositeFactor = 15;
            bFactorIncreasing = FALSE;
        }
        else
        {
            dwCompositeFactor++;
            ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_FACTOR, &dwCompositeFactor);
        }
    }
    else
    {
        if (dwCompositeFactor == 0) 
        {
            bFactorIncreasing = TRUE;
        }
        else
        {
            dwCompositeFactor--;
            ATI3DCIF_ContextSetState (ghRC, C3D_ERS_COMPOSITE_FACTOR, &dwCompositeFactor);
        }
    }

    // Update texture compositing factor string.

    sprintf (gszCompositeFactor, "Texture Composite Factor = %u", dwCompositeFactor);

} // UpdateFrameInfo 
