Sol's dirty DX7 tutorial
Initialisierung
Es gibt drei wirklich seltsame Dinge in Direct3D; Das erste ist die Initialisierung,
das zweite sind die Blend Modes und das dritte sind die Textur Formate. Als ich das erste
Mal von d3dx ("d3d helper lib" oder "d3d Hilfsbibliothek") wußte ich, das ich mir
dx7 zulegen muß. Bei den drei Problemen hilft d3dx bei der Initialisierung und den
Textur Formaten. Es hilft nicht bei den Blend Modes, was ich teilweise auch verstehen kann
(aber alle 3D Beschleuniger sollten in der Lage sein multiplikatives, applikatives und
annäherndes Blending IMO zu berechnen - sie machen es vielleicht auch, aber mit unterschiedlichen
Kombinationen der Parameter.. ich weiß nicht ob OpenGL oder Glide da besser sind).
Ich fange mit einem eher nackten Programm an.. Nur die Initialisierung und die normalen Windows Initialisierungen
(Tut euch keinen Zwang an mein win32 tutorial
zu lesen um mehr über Windows zu erfahren).
#define WIN32_LEAN_AND_MEAN
#include <windows.h> // windows stuff
#include <stdio.h> // standard IO
#include <stdlib.h> // standard C lib
#include <ddraw.h> // DirectDraw
#include <d3d.h> // Direct3D
#include <d3dx.h> // Direct3DX
/*
* Griddy0
* d3dx7 single threaded app
* sol/trauma 1999
*/
char progname[]="Griddy0 - Sol"; // our program name
HWND mainhWnd; // our program main window
HINSTANCE mainhInst; // and main instance
LPD3DXCONTEXT dxctx; // Direct3DX context handle.
LPDIRECT3DDEVICE7 d3dd; // Direct3d device
LPDIRECT3D7 d3d; // Direct3d itself
LRESULT CALLBACK WindowProc(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) {
if (uMsg==WM_DESTROY) { // kill command
d3d->Release(); // release d3d
d3dd->Release(); // release d3d device
D3DXUninitialize(); // shut down d3dx
exit(wParam); // bail out
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
void reindeer(void)
{
dxctx->Clear(D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER); // clear out
d3dd->BeginScene(); // all rendering should happen between begin and endscene..
// here be 3d rendering commands
// nothing yet though
d3dd->EndScene();
}
int APIENTRY WinMain(HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
WNDCLASSEX winclass;
HWND hWnd;
MSG msg;
int fs=0;
if (MessageBox(NULL,"Shall we do it in fullscreen mode?",
"Silly question",MB_YESNO)==IDYES) fs=1;
lpCmdLine=lpCmdLine; // remove warning
hPrevInstance=hPrevInstance; // remove warning
if (FAILED(D3DXInitialize())) return 0;
mainhInst=hInstance;
winclass.cbSize=sizeof(WNDCLASSEX);
winclass.style=CS_DBLCLKS;
winclass.lpfnWndProc=&WindowProc;
winclass.cbClsExtra=0;
winclass.cbWndExtra=0;
winclass.hInstance=hInstance;
winclass.hIcon=LoadIcon(NULL,IDI_APPLICATION);
winclass.hCursor=LoadCursor(NULL,IDC_ARROW);
winclass.hbrBackground=GetSysColorBrush(COLOR_APPWORKSPACE);
winclass.lpszMenuName=NULL;
winclass.lpszClassName=progname;
winclass.hIconSm=NULL;
if (!RegisterClassEx(&winclass))
return 0;
hWnd=CreateWindow(
progname,
progname,
WS_SYSMENU|WS_CAPTION|WS_BORDER|WS_OVERLAPPED|WS_VISIBLE|WS_MINIMIZEBOX,
CW_USEDEFAULT,
0,
640,
480,
NULL,
NULL,
hInstance,
NULL);
mainhWnd=hWnd;
if (FAILED(D3DXCreateContext(
D3DX_DEFAULT,
D3DX_CONTEXT_FULLSCREEN*fs, //windowed = 0
hWnd,
D3DX_DEFAULT,
D3DX_DEFAULT,
&dxctx))) return 0;
d3dd=dxctx->GetD3DDevice();
d3d=dxctx->GetD3D();
d3dd->SetRenderState(D3DRENDERSTATE_CULLMODE,D3DCULL_NONE ); // no cull
d3dd->SetRenderState(D3DRENDERSTATE_DITHERENABLE,TRUE); // dither on
d3dd->SetRenderState(D3DRENDERSTATE_ZENABLE,D3DZB_FALSE); // no zbuf
d3dd->SetRenderState(D3DRENDERSTATE_CLIPPING,FALSE); // no clipping
d3dd->SetRenderState(D3DRENDERSTATE_LIGHTING,FALSE); // no lighting
ShowWindow(hWnd,nCmdShow);
int frame=0;
int starttime;
starttime=GetTickCount();;
char str[200];
while (1) {
reindeer();
frame++;
int sec=GetTickCount()-starttime;
if (sec>0)
sprintf(str,"frame:%05d sec:%05d.%03d fps:%3.3f (alt-F4 quits)",
frame,sec/1000,sec%1000,(frame*1000.0)/sec);
dxctx->DrawDebugText((float)(2/640.0),(float)(2/480.0),0xffffff,str);
dxctx->UpdateFrame(0);
if(PeekMessage(&msg,hWnd,0,0,PM_REMOVE)) {
switch(msg.message) {
case WM_QUIT:
case WM_DESTROY: // kill command
d3d->Release(); // release d3d
d3dd->Release(); // release d3d device
D3DXUninitialize(); // shut down d3dx
return (msg.wParam);
default:
DefWindowProc(hWnd,msg.message,msg.wParam,msg.lParam);
}
}
}
}
Der Source sollte sich selbst erklären, aber da ich Tutorials hasse, die den Source einfach unkommentiert
den Leuten hinterlassen, sehen wir uns noch einmal die wichtigsten Stellen an.
dxctx->Clear(D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER);
d3dd->BeginScene();
d3dd->EndScene();
Der Darstellungsteil. Wenn ihr keinen Z-Buffer benutzt, macht es auch keinen Sinn ihn aufzuräumen..
und wenn ihr immer den ganzen Bildschirm beschreibt macht es auch keinen Sinn das Target zu löschen.
Alle Polygon-Darstellungen müssen zwischen begin und endscene erfolgen und es sollte immer nur ein
Paar pro Frame sein (für 'scene capture cards', die eure Szene nehmen und BSP Bäume für sich in Echtzeit
daraus generieren bevor sie es darstellen).
if (FAILED(D3DXCreateContext(
D3DX_DEFAULT,
D3DX_CONTEXT_FULLSCREEN*fs,
hWnd,
D3DX_DEFAULT,
D3DX_DEFAULT,
&dxctx))) return 0;
D3dx Kontext Erzeugung. Ihr müßt D3DXInit vorher aufrufen oder der Aufruf wird scheitern. Der Aufruf macht
in Wirklichkeit alle Initialisierungen, einschließlich Clippers für den Fenstermodus und Modi-Wechsel
und eine Menge andere Sachen, denen ihr früher oder später begegnet. Hofft, das es später ist.
Es gibt auch ein CreateContextEx, welches euch mehr Kontrolle über das gibt, was ihr erzeugt, aber wir
nutzen nur CreateContext. Das DirectX Doc sagt:
HRESULT D3DXCreateContext(
DWORD deviceIndex,
DWORD flags,
HWND hwnd,
DWORD width,
DWORD height,
LPD3DXCONTEXT* ppCtx);
Ihr könnt das Ausgabegerät mit deviceIndex näher spezifizieren oder einfach (wie wir) default nutzen,
um d3dx entscheiden zu lassen. Flags können angeben, ob man in den Vollbild-Modus wechseln soll oder
außerhalb des Bildschirmes berechnen will. Default bedeutet Fensterdarstellung. hwnd ist unser
primäres Fenster. Width und Height sind das übliche und Default Werte werden entweder von der Auflösung
abgeleitet, die wir angeben (default ist 640x480) oder der client area unseres Fensters.
Der letzte Parameter ist ein Zeiger zu unserem Context Handle, welches wir nutzen wollen.
d3dd=dxctx->GetD3DDevice();
d3d=dxctx->GetD3D();
d3dd->SetRenderState(D3DRENDERSTATE_CULLMODE,D3DCULL_NONE );
d3dd->SetRenderState(D3DRENDERSTATE_DITHERENABLE,TRUE);
d3dd->SetRenderState(D3DRENDERSTATE_ZENABLE,D3DZB_FALSE);
d3dd->SetRenderState(D3DRENDERSTATE_CLIPPING,FALSE);
d3dd->SetRenderState(D3DRENDERSTATE_LIGHTING,FALSE);
Als erstes fordern wir Handles zu einem direct3ddevice und direct3d an und
anschließend stellen wir einige Optionen des 3D Devices ein. Wir setzen Culling auf
aus, da wir das wahrscheinlich schon tun werden, bevor wir alles darstellen und die
Hardware (oder die Treiber) könnten Zeit verschwenden indem sie es nochmal durchrechnen.
Wir aktivieren Dithering (der default Modus ist 16 Bit) und deaktivieren den Z-Buffer
(natürlich solltet ihr ihn aktivieren, wenn ihr ihn nutzen wollt! (was eigentlich ziemlich
wahrscheinlich ist)). Dann, nur um sicher zu gehen, schalten wir Clipping und Beleuchtung
aus, damit die Treiber nichts vermasseln. (Die Art wie wir hier mit Clipping arbeiten
kann funktionieren. Auf manchen Karten vielleicht aber auch nicht, also sollten
wir auf Nummer sicher gehen - d3d macht sein Clipping bevor es in den Bildschirmspeicher geschrieben
wird, da wir aber direkt in den Bildschirmspeicher schreiben ist es dafür ein kleines bischen spät).
dxctx->DrawDebugText((float)(2/640.0),(float)(2/480.0),0xffffff,str);
dxctx->UpdateFrame(0);
Es gibt noch zwei andere Dinge die d3dx nützlich machen; man kann sehr einfach Debug-Texte einfügen
(welche aus irgenwelchen perversen Gründen Offset Koordinaten als Floats von 0 bis 1 nutzen) und
ihr könnt zwischen Bildschirmseiten mit einem Aufruf hin- und herwechseln ohne euch Gedanken zu machen,
ob ihr im Vollbild-Modus seid oder nicht.
Nun nehmt den Source und Kompiliert ihn. Ihr bekommt wahrscheinlich dutzende von unresolved externals.
Ihr müsst mindestens die dxguid.lib (Global Unique Identifiers), ddraw.lib und d3dim.lib (D3D Immediate
Mode) linken. Wenn ihr von der Kommandozeile aus kompiliert, fügt noch gdi32.lib und user32.lib
zusätzlich hinzu. Ihr solltet dann eine ausführbare Datei erhalten die, wenn man sie startet, unsere
Statusinformationen zeigt (mit Frames per Second Zähler).
Das nächste Mal fügen wir ein paar Polys hinzu.