email sol follow sol rss feed of the blog wishlist Sol::Tutorials

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.

Site design & Copyright © 2021 Jari Komppa
Possibly modified around: June 01 2010