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.