#define WIN32_LEAN_AND_MEAN // Dieses #define klammert selten benötigte Windows-Funktionen aus
 
#include <windows.h>
#include <D3DX8.h>
 
#define WNDCLASSNAME "DirectX8 Einführung 1"
 
//
// Einbinden der DirectX-Bibliotheken. Sollte man eigentlich in den
// Projekteinstellungen tätigen, aber so kann man die .cpp-Datei einfach in
// ein neues Projekt einfügen und es funktioniert.
//
#pragma comment(lib, "d3d8.lib")
#pragma comment(lib, "d3dx8.lib")
 
//
// Funktionsprototypen
//
void createWindow();
void removeWindow();
void initDirect3D();
void releaseDirect3D();
void render();
 
void exitOnError(const char *ErrorMessage);
void exitOnError(HRESULT res, const char *ErrorMessage);
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam);
 
//
// Globale Variablen
//
HWND                 g_HWnd = NULL; // Das Handle unseres Fensters für den Empfang von Windows-Nachrichten
HINSTANCE            g_Instance = NULL; // Das Handle unseres Programms
 
LPDIRECT3D8          g_pD3D = NULL; // Zeiger auf unsere Instanz von Direct3D
IDirect3DDevice8    *g_pD3DDevice = NULL; // Zeiger auf unsere Instanz des Direct3DDevices
 
bool                 g_Quit = false; // Zeigt an, ob unser Programm verlassen werden soll, weil der Benutzer [ESC] gedrückt hat
int                  g_Green = 0; // Der Grünanteil der Farbe, mit welcher der Hintergrund gefüllt wird
 
 
/*--------------------------------------------------------------------------------
 
    Name: initDirect3D
 
    Description:
        Initialisiert Direct3D und das Direct3DDevice.
 
--------------------------------------------------------------------------------*/
void initDirect3D()
{
    HRESULT                 res;
    D3DPRESENT_PARAMETERS   PresentParams;
 
    //
    // Unsere Instanz von Direct3D erzeugen
    //
    g_pD3D = Direct3DCreate8(D3D_SDK_VERSION);
 
    if (g_pD3D == NULL)
        exitOnError("Direct3DCreate8() fehlgeschlagen!\n");
 
    // Die Struktur vor dem Benutzen löschen
    ZeroMemory(&PresentParams, sizeof(PresentParams));
 
    //
    // Festlegen der Parameter für unser Direct3DDevice
    //
    PresentParams.Windowed = FALSE; // Unser Programm soll im Vollbildmodus laufen
    PresentParams.hDeviceWindow = g_HWnd; // Handle unseres Fensters
    PresentParams.BackBufferCount= 1; // Wir wollen 1 Backbuffer für Double Buffering
    PresentParams.SwapEffect = D3DSWAPEFFECT_DISCARD; // Nach dem Anzeigen interessiert uns der Backbuffer nicht mehr
    PresentParams.BackBufferWidth = 640; // Breite
    PresentParams.BackBufferHeight = 480; // Höhe
    PresentParams.BackBufferFormat = D3DFMT_R5G6B5; // Farbtiefe. Diese Farbtiefe wird von fast allen Grafikkarten unterstützt
 
    //
    // Erzeugen der Direct3DDevice
    //
    res = g_pD3D->CreateDevice
    (
        D3DADAPTER_DEFAULT,
        D3DDEVTYPE_HAL,
        g_HWnd,
        D3DCREATE_SOFTWARE_VERTEXPROCESSING,
        &PresentParams,
        &g_pD3DDevice
    );
 
    if (FAILED(res))
        exitOnError(res, "CreateDevice() fehlgeschlagen!\n");
}
 
/*--------------------------------------------------------------------------------
 
    Name: releaseDirect3D
 
    Description:
        Gibt Direct3D und das Direct3DDevice frei.
 
--------------------------------------------------------------------------------*/
void releaseDirect3D()
{
    // Wurde ein Direct3DDevice erstellt?
    if (g_pD3DDevice)
    {
        // Ja! Dann freigeben
        g_pD3DDevice->Release();
        g_pD3DDevice = NULL;
    }
 
    // Wurde eine Instanz von Direct3D erzeugt?
    if (g_pD3D)
    {
        // Ja! Dann freigeben
        g_pD3D->Release();
        g_pD3D = NULL;
    }
}
 
/*--------------------------------------------------------------------------------
 
    Name: render
 
    Description:
        Zeigt die Grafik auf dem Bildschirm an.
 
    Remarks:
        Momentan wird einfach nur der Bildschirm gelöscht.
 
--------------------------------------------------------------------------------*/
void render()
{
    // Den Grünton zum Füllen des Hintergrundes verändern
    g_Green = (g_Green + 1) % 256;
 
    //
    // Den Hintergrund mit einem Grünton füllen
    //
    g_pD3DDevice->Clear
    (
        0, // Anzahl der Rechtecke, die zum Löschen angemeldet werden. Muss 0 sein, wenn der 2. Parameter NULL ist
        NULL, // Zeiger auf Array mit Rechtecken. Bei NULL nimmt Direct3D ein Rechteck für den ganze Bildschirm an
        D3DCLEAR_TARGET, // Einfach nur Hintergrund löschen
        D3DCOLOR_XRGB(0, g_Green, 0), // Hintergrundfarbe angeben
        0.0f, // Füllwert für den ZBuffer (wird momentan nicht benutzt)
        0 // Füllwert für den Stencil-Buffer (wird momentan nicht benutzt)
    );
 
    g_pD3DDevice->BeginScene();
 
    //
    // Momentan wird noch keine eigentliche Grafik gezeichnet
    //
 
    g_pD3DDevice->EndScene();
 
    // Den neu gezeichneten Backbuffer zum Frontbuffer machen und damit anzeigen
    g_pD3DDevice->Present(NULL, NULL, NULL, NULL);
}
 
/*--------------------------------------------------------------------------------
 
    Name: createWindow
 
    Description:
        Erzeugt das Fenster des Programms.
 
--------------------------------------------------------------------------------*/
void createWindow()
{
    WNDCLASSEX wc;
 
    //
    // Erzeugen unserer Fensterklasse
    //
 
    // Struktur mit Eigenschaften unserer Fensterklasse füllen
    wc.cbSize           = sizeof(WNDCLASSEX);
    wc.style            = CS_OWNDC;
    wc.lpfnWndProc      = WindowProc;
    wc.cbClsExtra       = 0;
    wc.cbWndExtra       = 0;
    wc.hInstance        = g_Instance;
    wc.hIcon            = LoadIcon(NULL, IDI_APPLICATION);
    wc.hCursor          = LoadCursor(NULL, IDC_ARROW);
    wc.hbrBackground    = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wc.lpszMenuName     = NULL;
    wc.lpszClassName    = WNDCLASSNAME;
    wc.hIconSm          = LoadIcon(NULL, IDI_APPLICATION);
 
    // Die neue Fensterklasse registrieren
    if (!RegisterClassEx(&wc))
        exitOnError("RegisterClassEx() fehlgeschlagen!\n");
 
    //
    // Fenster für die Applikation erstellen
    //
    g_HWnd = CreateWindowEx
    (
        NULL, 
        WNDCLASSNAME,
        "Kapitel 1",
        WS_OVERLAPPEDWINDOW | WS_VISIBLE,
        0, 0,
        400, 400,
        NULL,
        NULL,
        g_Instance,
        NULL
    );
 
    if (!g_HWnd)
        exitOnError("CreateWindowEx() fehlgeschlagen!\n");
 
    //
    // Das neue Fenster anzeigen und fokusieren
    //
    ShowWindow(g_HWnd, SW_SHOW);
    UpdateWindow(g_HWnd);
    SetFocus(g_HWnd);
}
 
 
/*--------------------------------------------------------------------------------
 
    Name: removeWindow
 
    Description:
        Meldet das Fenster des Programms ab.
 
--------------------------------------------------------------------------------*/
void removeWindow()
{
    // Wurde ein Fenster erzeugt?
    if (g_HWnd)
    {
        // Ja! Dann abmelden!
        if (!DestroyWindow(g_HWnd))
            MessageBox(NULL, "Destroy Window Failed", WNDCLASSNAME, MB_OK | MB_ICONERROR | MB_TOPMOST);
 
        g_HWnd = NULL;
    }
}
 
/*--------------------------------------------------------------------------------
 
    Name: exitOnError
 
    Description:
        Gibt eine Fehlermeldung aus.
 
    Parameters:
        pErrorMessage - Zeiger auf den Text der den Fehler beschreibt
 
--------------------------------------------------------------------------------*/
void exitOnError(const char *pErrorMessage)
{
    //
    // Eventuell angemeldete Ressourcen freigeben
    //
    releaseDirect3D();
    removeWindow();
 
    // Die Fehlermeldung in der Debugausgabe ausgeben
    OutputDebugString(pErrorMessage);
    OutputDebugString("\n");
 
    // Die Fehlermeldung ebenfalls als MessageBox() anzeigen
    MessageBox(NULL, pErrorMessage, WNDCLASSNAME, MB_OK);
 
    // Das Programm mit einem Fehlercode verlassen
    exit(-1);
}
 
/*--------------------------------------------------------------------------------
 
    Name: exitOnError
 
    Description:
        Gibt eine Fehlermeldung aus.
 
    Parameters:
        res - DirectX Fehlercode
        pErrorMessage - Zeiger auf zusätzliche Nachricht, die den Fehler
                        beschreibt
 
    Remarks:
        Ruft zur eigentlichen Ausgabe exitOnError(const char *pErrorMessage) auf.
 
--------------------------------------------------------------------------------*/
void exitOnError(HRESULT res, const char *pErrorMessage)
{
    char Buffer[256];
 
    //
    // Eine Beschreibung des DirectX-Fehlercodes anfordern
    //
    D3DXGetErrorStringA(res, Buffer, 200);
 
    //
    // Mit der eigentlichen Fehlermeldung verbinden
    //
    strcat(Buffer, "\n");
    strcat(Buffer, pErrorMessage);
 
    //
    // Programm mit Fehlermeldung beenden
    //
    exitOnError(Buffer);
}
 
 
/*--------------------------------------------------------------------------------
 
    Name: WindowProc
 
    Description:
        Behandlungsroutine für eingehende Nachrichten unseres Fensters.
 
    Parameters:
        hwnd - Das Handle des Fensters, für das die Nachricht gedacht ist.
        msg - Die Nachrichten-ID, die an das Fenster gesand wurde.
        wparam - Parameter 1 der Nachricht.
        lparam - Parameter 2 der Nachricht.
 
    Return Value:
        0, wenn die Nachricht bearbeitet wurde.
 
    Remarks:
        Auf den Tastendruck [ESC] wird das Flag zum Beenden des Programms
        gesetzt.
 
--------------------------------------------------------------------------------*/
LRESULT CALLBACK WindowProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam)
{
    switch(msg)
    {
        //
        // Auf Tastendruck von [ESC] das Flag zum Beenden des Programms setzen
        //
        case WM_KEYDOWN:
            switch(wparam)
            {
                case VK_ESCAPE:
                g_Quit = true;
            }
 
            return 0;
 
        //
        // Bei eingehender WM_CLOSE-Nachricht das Programm beenden
        //
        case WM_CLOSE:
            g_Quit = true;
 
            return 0;
 
        case WM_DESTROY:
            PostQuitMessage(0);
 
            return 0;
    }
 
    //
    // Alle unbehandelten Nachrichten an die Standard-Behandlung weiterleiten
    //
    return (DefWindowProc(hwnd, msg, wparam, lparam));
}
 
 
/*--------------------------------------------------------------------------------
 
    Name: WinMain
 
    Description:
        Hauptschleife
 
    Parameters:
        Instance - Handle unseres Programms
        PrevInstance - Handle einer zuvor geöffneten Instanz unseres Programms.
                       Meist NULL, weil das Programm nur einmal geöffnet wird.
        pCmdLine - Die Kommandozeilenparameter
        Show - Gibt an, ob das Programm normal, maximiert, ninimiert angezeigt
               werden soll. Für uns nicht wichtig.
 
    Return Value:
        Exit-Code des Programms. Bei fehlerfreier Beendigung 0.
 
--------------------------------------------------------------------------------*/
int APIENTRY WinMain(HINSTANCE Instance, HINSTANCE PrevInstance, LPSTR pCmdLine, int Show)
{
    MSG msg;
 
    // Wir merken uns das Handle unserer Instanz, da wir es später noch
    // benötigen
    g_Instance = Instance;
 
    // Fenster der Applikation erzeugen
    createWindow();
 
    // Direct3D initialisieren
    initDirect3D();
 
    //
    // Nachrichten "pumpen" bis der Benutzer das Programm verlassen will
    //
    while (g_Quit == false)
    {
        // Liegen Nachrichten in der Nachrichten-Schlange?
        if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) == TRUE)
        {
            // Ja! Dann Standard-Weiterleitung
            TranslateMessage(&msg);
            DispatchMessage(&msg);
        }
 
        // Unsere Grafik anzeigen
        render();
    }
 
    // Direct3D freigeben
    releaseDirect3D();
 
    // Fenster der Applikation entfernen
    removeWindow();
 
    // Programm mit Fehlercode 0 (kein Fehler) beenden
    return 0;
}