Zusammenfassung:
->Step1: Creating a Window
In Qt null Problem: Wir erstellen eine Klasse die unsere DirectX-Funktionalität kapselt
UND DIE ERBT GANZ EINFACH VON QWIDGET!
Code: Alles auswählen
#ifndef M_DIRECTX9
#define M_DIRECTX9
#include <QWidget>
class QDirectX9 : public QWidget {
Q_OBJECT
};
#endif
->Step2: Initializing DirectX
Zur Erzeugung des DirectXDevice wird die winId des Widgets benötigt.
Die Initialisierung wurde in eine eigene Methode: Initialize() implementiert. Der Beispielcode von MS in der SDK kann 1:1 übernommen werden.
Für Qt wichtig: Includieren der Headerfiles: d3d9.h und d3d9types.h.
Für den Zugriff auf externe Biblotheken muss die Projektdatei um diesen Satz erweitert werden:
win32:LIBS Pfad_Compiler/lib/libd3d9.a
das gilt für den MinGW. Für andere Compiler die entsprechende LibFile eintragen.[/b]
->Step3: Handling System Messages
Windows-like wäre ideal, wenn die Render()Methode, das ist der eigentliche Motor, der die Szenarios in Gange hält, im App-Main-Loop unterbracht wäre. Der Main-Loop wird aber von QApplication gekapselt.
Alternativ bietet MS die Möglichkeit, die Render()-Methode aus dem Event-Callback des unter winId genannten Windows aufzurufen. DirectX ruft WM_Paint auf, wenn die Szenarios refreshed werden müssen. Hier ist ein Ansatz. Wir reimplementieren: paintEvent und fügen da die Render()-Methode ein.
So dann, die QDirectX9.h
Code: Alles auswählen
#ifndef M_DIRECTX9
#define M_DIRECTX9
#include <d3d9.h>
#include <d3d9types.h>
#include <QWidget>
class QPaintEvent; //vorwärtsdeklaration von QPaintEvent
class QDirectX9 : public QWidget{
Q_OBJECT
public:
Render();
private:
void paintEvent(QPaintEvent*); //<-Deklaration
};
#endif
Implementiert in der QDirectX9.cpp:
Code: Alles auswählen
#include "QDirectX9.h"
#include <QPaintEvent>
void QDirectX9::paintEvent(QPaintEvent *event){
Render();
};
Wenn im Bildschrim die untere linke Ecke gezeichnet wird, wird DirectX eine WM_Paint Message posten, aber auch andere Events, die ein Neuzeichnen erfordern bewirken den Aufruf der Render()-Methode - aber:
Unser Widget wird sich nicht von Render() bändigen lassen, weil es eine eigene Backgroundrole spielt. Das bewirkt, dass die Ausgabe von Render() durch das Widget übertüncht werden. Man merkt das, wenn man das Window zieht, dass kurz die Figur gezeichnet wird und aber sofort wieder im Windows-grau verschwindet. Man muss dem Widget es abgewöhnen, die Hintergrund Rolle abzuspielen. Deshalb wurde in der Initialize()-Methode:
Code: Alles auswählen
bool QDirectX9::Initialize(){
IDirectX = Direct3DCreate9(D3D_SDK_VERSION);
if(IDirectX == NULL) return false;
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
HRESULT res = IDirectX->CreateDevice(D3DADAPTER_DEFAULT,
D3DDEVTYPE_HAL, winId(), D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &IDevice);
if(res != D3D_OK){
IDirectX->Release();
return false;
}
IsDxInit = true;
setGeometry(10,10,200,200);
setAttribute(Qt::WA_OpaquePaintEvent,true);
return true;
};
Die Anweisung: setAttribute(Qt::WA_OpaquePaintEvent,true); aufgenommen, die genau das bewirkt, dass das Widget das Zeichnen des Hintergrunds uns überlässt! Auch hier wurde der Code aus der SDK fast 1:1 übernommen. winId() ist hWnd unseres DirectX-Windows.
->Step4: Rendering and Displaying a Scene
Das ist ein Kunstgriff auf Tutorial Teil2 -> Vectors, wird in Teil 2 des Tuts behandelt.
->Step5: Shutting down
War ein schwerer Fall für den Destruktor! Klar ist: DirectX ist eine ActiveX-Komponente und Teile darin sind COM-Klassen. Es ist Vertrag, dass wenn man eine COM-Class aufruft, sie auch wieder löschen muss. Jede Class, jede Komponente beginnt mit:
QueryInterface()
Addref()
Release()
Ein Aufruf einer Class bedeutet, dass der Instanzzähler in Addref um 1 erhöht wird. Die Class wird solange im Speicher gehalten, bis der Zähler auf 0 gesetzt wird und das allein bewirkt die Methode: Release().
Für Qt bedeutet das, dass Qt der Client ist, der anfordert. Also wird Addref() gesetzt, was die Class selbst tut. Was Qt und wir tun müssen ist:
Wenn wir die Class nicht mehr brauchen einfach Release() aufzurufen. COM terminiert das selbst aus dem Speicher.
Somit ist der Destruktor also:
Code: Alles auswählen
QDirectX9::~QDirectX9(){
if(IDevice !=0) IDevice->Release();
if(IDirectX !=0) IDirectX->Release();
};
Ich glaube, das dies keiner weiteren Erklärung mehr bedarf

Die restlichen Aufräumarbeiten kann man getrost Qt überlassen. Für DirectX sind wir aber, immer daran denken!, selbst voll verantwortlich.