320 likes | 431 Views
3D-Grafik mit der TriBase-Engine. Universität zu Köln Softwaretechnologie II WS 11/12 Dozent: Prof. Dr. M. Thaller Referentin: Nadya Steinert. Automatische Initialisierung von Direct3D Statusänderungen minimieren Texturverwaltung Vertex- und Indexbuffer einfacher erstellen
E N D
3D-Grafik mit der TriBase-Engine Universität zu Köln Softwaretechnologie II WS 11/12 Dozent: Prof. Dr. M. Thaller Referentin: Nadya Steinert
Automatische Initialisierung von Direct3D • Statusänderungen minimieren • Texturverwaltung • Vertex- und Indexbuffer einfacher erstellen • D3DX-Effekte leichter verwalten
Die Klasse tbDirect3D • Dies ist eine Singleton-Klasse, von der nur eine Instanz erzeugt werden kann • Sie verkörpert die vorher benannten Funktionen • Die Funktion tbConfigDialog sammelt Informationen über die Direct3D-Einstellungen: Adapter, Videomodi, Oberflächenformate usw. Die Methode tbDirect3D:: Init initialisiert Direct3D und erstellt das Fenster; ihr werden die tbConfig-Struktur und Fenstertitel und -icon übergeben Die Exit- Methode setzt alle Texturen zurück und löscht alle Direct3D-Schnittstellen
Klassendeklaration- Variablen • m_bInitialized (BOOL) – am Anfang und nach Herunterfahren FALSE; • Variable verhindert mehrfaches Aufrufen der Init-Funktion • Zwei Zeigerfür dieSchnittstellen IDirect3D9 und IDirect3DDevice9 • Die D3DCAPS9-Struktur des verwendeten Gerätes speichern • Eine HWND-Variable für das Fenster • m_bOwnWindow (BOOL) falls der Benutzer eigenes Fenster verwendet • Für Kapselung der Methoden SetRenderState, GetRenderState, SetTextureStageState usw. braucht man Tabellen; DWOR- und float-Version • Ein Array vom Typ PDIRECT3DBASETEXTURE9 mit 8 Elementen speichert die eingesetzten Texturen • m_vScreenSize (tbVector2) speichert die Größe des Bildpuffers in Pixeln
Klassendeklaration- Methoden • Init-Methode erwartet zuerst einen Zeiger auf tbConfig , erstellt ein Fenster und initialisiert die IDirect3DDevice9-Schnittstelle; dann ein Fenstername, ein HWND-Parameter für Benutzerfenster, ein HICON-Parameter • Kapselungsmethoden für die verschiedenen States die mit den DWORD – und float- Tabellen arbeiten • Verschiedene Hilfsmethoden: IsInitialized, GetWindow, GetDirect3D usw. • Capture-Methode fragt den aktuellen Status der Geräteschnittstelle ab
Die Funktion tbDuConfigDialog • sie ruft den Konfigurationsdialog auf • Füllt die tbConfig-Struktur aus, die die gesamte Konfiguration von TriBase-Engine beinhaltet
Die Init-Methode • Erzeugt das Anwendungsfenster • Eine IDirect3D9-Schnittstelle • Ruft CreateDevice auf, um eine IDirect3DDevice9-Schnittstelle zu erhalten • Fragt die Gerätefähigkeiten( D3DCAPS9) ab • Speichert den aktuellen Status der Tabellen durch die Capture-Methode • Setzt Standard-Render und Sampler-States • Setzt m_bInitialized auf true
Speichern des aktuellen Status mit Capture() • Diese Methode geht alle Render-States, Sampler-States usw. durch, deren Werte in Tabellen gespeichert sind, fragt sie ab und speichert sie • Doppelte Statusänderungen werden verhindert indem die Set-Methoden prüfen, ob der Status schon gesetzt ist und wenn dass der Fall ist, wird sofort unterbrochen • // Wenn das Render-State schon gesetzt ist, direkt abbrechen • if(m_RS.adwRS[RS] == dwValue) return TB_OK;
Die Exit-Methode setzt alle Texturen, Index- und Vertex-Buffer zurück, gibt die zwei Schnittstellen frei(D3D und D3DDevice) • Methoden wie SetTextue() erhöhen den Zähler der ihr übergebenen Schnittstelle und müssen unbedingt zurückgesetzt werden, damit keine Speicherlecks entstehn • Um auf die IDirect3DDevice9 zugreifen zu können, wird der ‚->‘-Operator überladen • PDIRECT3DDEVICE9 operetor ->{return m_pD3DDevice;} • D3D->DrawPrimitive() //direkt Methoden von IDirect3DDevice9 aufrufen
tbTextureManager • Sorgt dafür , dass keine Textur zwei Mal geladen wird • Alle geladenen Texturen werden in einer Liste mit deren Dateinamen festgehalten • Wenn eine bereits geladene Textur wieder gefragt ist, wird der Referenzzähler der Texturschnittstelle erhöht • Wenn der Referenzzähler null erreicht , wird die Textur aus der Liste gelöscht • Texturen können aus verschiedenen Quellen geladen werden: aus Speicher, einer Ressource oder aus ZIP_Archiv
Klassendefinition von tbTextureManager • Es gibt eine Liste, wo alle Texturen eingetragen werden • jeder Textureintrag wird in die Struktur tbTextureListEntry gespeichert • struct TRIBASE_API tbTextureListEntry • { • BOOLbExists;// Existiert diese Textur? • PDIRECT3DBASETEXTURE9pTexture;// Die Texturschnittstelle • characSourceFilename[256];// Quelldateiname • intiWidth;// Breite der Textur • intiDepth;// Tiefe der Textur • intiHeight;// Höhe der Textur • intiNumMIPLevels;// Anzahl der MIP-Mapping-Ebenen • D3DFORMATFormat;// Oberflächenformat der Textur • DWORDdwUsage;// Verwendungszweck • D3DPOOLPool;// Speicherklasse • DWORDdwFilter;// Bildfilter (beim Laden) • DWORDdwMIPFilter;// MIP-Map-Filter (beim Laden) • D3DCOLORColorKey;// Color-Key (beim Laden) • };
Klassendefinition -Variablen • Für die Texturen wird eine nicht verkette Liste benutzt; der Zeiger m_pTextureList zeigt auf eine tbTextureListEntry-Struktur; Speicher wird dynamischreserviert • m_iListSize speichert die aktuelle Größe der Texturliste • m_iNumTexture- Anzahl der geladene Texturen
Klassendefinition- Methoden • Init-Methode erwartet als Parameter die Ausgangsgröße der Texturliste und reserviert genug Speisher • SetListSize verändert die Listengröße, wenn z.B. kein Platz für eine neue Textur da ist • GetTextureIndex erwartet als Perameter eine Texturschnitstelle, sucht sie in der Liste und liefert den Texturindex zurück • GetNewIndex liefert den nächsten freien Index in der Liste zurück, wenn kein Platz -1; es wird nach dem ersten Listeneintrag gesucht bei dem m_bExist FALSE ist • ReleaseTexture erwartet auch eine Texturschnittstelle, sucht die Textur und ruft Release auf; wenn Referenzzähler null, Textur wird gelöscht • DeleteAllTexture geht die gesamte Liste durch und ruft für jede Textur so lange Release auf bis Referenzzähler null
Klassendefinition -Methoden • AddTextureToList fügt eine Textur zur Liste hinzu, und erweitert diese, wenn kein Platz mehr da ist • Load-Methoden für Standard-, Würfel- und Volumentexturen; Für jeden Typ vier verschiedene Versionen, die erwarten Dateiname, Speicheradresse,Ressourcenangabe, oder virtuelle Datei; Die ersten drei Methoden erzeugen eine virtuelle Datei aus der Quelle und rufen die Vierte Version auf; sie haben den gleichen Namen und verschiedene Parameter(immer zuerst die Quellangabe) • Get-Methoden bekommen die Parameter einer Textur und durchsuchen die Liste nach einer Übereinstimmung, passende Textur wird zurückgeliefert(Referenzzähler wird erhöht); wenn Textur nicht vorhanden, wird die entsprächende Load-Methode aufgerufen
tbVertexBuffer und tbIndexBuffer • Informationen zum Art des Puffers • Verwendungszweck: 0 ist Standard; D3DUSAGE_DYNAMIC(dynamischer Puffer);D3DUSAGE_WRITEONLY(kein Lesezugriff; • Speicherklasse: die vier Flags D3DPOOL_DEFAULT,D3DPOOL_MANAGED,D3DPOOL_SYSTEMMEM und D3DPOOL_SCRATCHstehen zur Auswahl • Sperrmethode: 0, D3DLOCK_NOSYSLOCK, D3DLOCK_DISCARD, D3DLOCK_NOOVERWRITE und D3DLOCK_READONLY
Funktionsweise von den Buffern • Beim Initialisieren der Klasse gibt der Benutzer den Verwendungszweck und die Speicherklasse an • Die Größe des Index- und Vertex-Buffer kann eventuell angepasst werden • Beide Klassen fertigen sich intern eine Kopie des gesamten Puffers an; mit SetVertex und SetIndex kann der Benutzer sie verändern, wenn die Veränderung durchgeführt werden soll, wird Update() aufgerufen • Der veränderte Speicherbereich wird gesperrt und die Daten aus der internen Kopie werden in den Puffer kopiert • Bei D3DUSAGE_WRITEONLY wird nur aus der Kopie im Systemspeicher gelesen und im Puffer nur geschrieben; GetVertex() fragt Vertizes von der internen Kopie ab
Klassendefinition- Variablen(VertexBuffer) • m_pVertexBuffer vom Typ PDIRECT3SVERTEXBUFFER9 • m_pBuffer der auf die interne Kopie zeigt, vom Typ void • DWORD m_dwSize speichert die Größe des Puffers in Bytes • DWORD m_dwVertexSize- die Größe eines einzelnen Vertex • DWORD m_dwMaxVertices – Anzahl der Vertizes im Puffer • DWORD dwFVF : der Vertexformat, beim Indexbuffer m_IndexFormat (D3DFMT_INDEX16 oder D3DFMT_INDEX32) • DWORD m_dwUsage : Verwendungszweck des Puffers, D3DUSAGE_WRIETEONLY ist immer gesetzt • D3DPOOL m_Pool: die Speicherklasse, in der sich der Vertex-Buffer befindet
Klassendefinition- Variablen(VertexBuffer) • DWORD dwFirstVertex, dwLastVertex speichern die Nummern des ersten und des letzten veränderten Vertex; SetVertex() setzt die Variablen, um später den zu sperrenden Bereich zu ermitteln; nach dem Sperren hat dwFirstVertex den höchst möglichen Wert und dwLastVertex 0 • DWORD m_dwCursor speichert die Nummer des nächsten Vertex, der mit AddVertex() gesetzt wird
Klassendeklaration- Methoden • Init() generiert die IDirect3DVertexBuffer9-Schnittstelle und die interne Kopie; die erste Version der Methode erwartet die Größe des Vertex-Buffers, die Vertexgröße, den FVF-Bezeichner, Vervwendungszweck und Speicherklasse • Die zweite Version erwartet einen bereits existierenden Vertex-Buffer(PDirect3DVertexBuffer9) und die Größe eines Vertex; aus diesem wird die tbVertexBuffer- Instanz initialisiert; GetDesc() fragt die Beschreibung des Vertex-Buffers ab; erste Funktion baut auf die zweite auf • Exit() gibt die Schnittstelle und die interne Speicherkopie frei; wird einmal vom Destruktor und einmal am Anfang von Init() aufgerufen • SetVertex() erwartet die Nummer des zu setzenden Vertex(0 ist der erste) und den Vertex selbst; der angegebene Vertex wird in die Speicherkopie geschrieben und m_dwFirstVertex und m_dwLastVertex werden aktualisiert
Klassendeklaration- Methoden • SetVertices() setzt mehrere Vertizes; erster Parameter: Index des ersten zu setzenden Vertex; dann Anzahl der Vertizes und schließlich Zeiger auf die Vertizes • AddVertex() fügte dem Vertex-Buffer einen neuen Vertex hinzu und liefert seinen Index zurück; die Nummer des nächsten Vertex ist im m_dwCursor • AddVertices() fügt gleich mehrere Vertizes hinzu, die im zweiten Parameter angegeben sind; Erster Parameter- die Anzahl • SetCursor() setzt den Vertexcursor auf den durch den Parameter angegebenen Wert; wenn Vertex-Buffer voll Cursor wird per Hand zurückgesetzt • GetVertex() liefert den durch den Index angegebenen Vertex • GetVertices() : erste Parameter gibt den Index des ersten abzufragenden Vertex an; 2. Parameter: Anzahl der abzufragenden Vertizes; dritter: Zeiger auf den Speicher Bereich, wo es hinkopiert werden soll
Klassendeklaration- Methoden • Update() schreibt die veränderte Vertizes in den Vertex-Buffer; biem dynamischen Puffer Sperflag D3DLOCK_DISCARD; m_dwFirstVertex und m_dwLastVertex werden zurückgesetzt • Verschiedene Inline- Methoden
tbEffect • Optionen zum zeichnen eines Objekts oder eins Materials werden in eine Effekt-Datei gespeichert werden • Der Benutzer kann selbst festlegen Welche Techniken benutzt werden oder überlässt das der Klasse • Ein globales Effekt-Pool ermöglicht es , das verschiedene Effekte ihre Parameter teilen; wird in Init() erstellt
Klassendeklaration- Variablen • LPD3DXEFFECT m_pEffect : die Effekt-Schnittstelle • D3DXEFFECT_DESC m_Desc :die Effektbeschreibung • BOOL m_bStateSaved: TRUE , wenn beim Aufruf von Begin() angegeben wurde, dass dewr aktuelle Status gespeichert werden soll • BOOL m_bCaptureOnFinish: TRUE; WENN BEIM Beenden des Effekts automatisch Capture() aufgerufen werden soll
Klassendeklaration - Methoden • 5 Init-Methoden: Initialisierung aus einem String , der den gesamten Code enthält; aus einer virtuellen Datei; einer echten Datei, einem Speicherbereich oder einer Ressource • Exit() löscht die Schnoittstellen und verringert die Referenzzähler der Texturen • SetTechnique: setzt eine Technik , die durch ihre Nummer angegeben wird; gibt man -1 an, sucht die Methode die erste gültige Technik aus • Begin() : erster BOOL-Parameter bestimmt, ob die Änderungen später rückgängig gemacht werden sollen; der zweite BOOL-Parametergibt an, ob die internen Tabellen der Render-States, Texturschicht-State usw. in der tbDirect3D-Klasse aktualisiert werden sollen • Die beide Parameter werden in m_bStateSaved bzw. in m_bCaptureOnFinish gespeichert;Rückgabewert von Begin() ist die Anzahl der benötigten Durchgänge für den Effekt • End() beendet den Effekt • Pass() aktiviert den durch seine Nummer angegebenen durchgang
Ein Beispiel • Die Wasseroberfläche wird in eine Gitter eingeteilt • Die Höhe jedes Vertex wird einmal pro Frame aktualisiert und hängt von der abgelaufene Zeit und von x- un z-position ab • Wellen lassen sich durch Sinus-Funktionen simulieren • Globale Variablen und Strukturen: zwei Strukturen, eine für die Wasser-Vertizes und eine für die SkyBox-Vertizes • // Globale Variablen • tbConfigg_Config;// Konfigurationsstruktur • PDIRECT3DTEXTURE9g_pWaterTexture = NULL;// Wassertextur • PDIRECT3DCUBETEXTURE9g_pEnvMap = NULL;// Umgebungstextur • tbEffect*g_pWaterEffect = NULL;// Wassereffekt • tbEffect*g_pSkyBoxEffect = NULL;// Sky-Box-Effekt • tbVertexBuffer*g_pWaterVB = NULL;// Wasser-Vertex-Buffer • tbIndexBuffer*g_pWaterIB = NULL;// Wasser-Index-Buffer • tbVertexBuffer*g_pSkyBoxVB = NULL;// Sky-Box-Vertex-Buffer • tbIndexBuffer*g_pSkyBoxIB = NULL;// Sky-Box-Index-Buffer • const intg_iResolution = 64;// Die Auflösung der Wasseroberfläche • floatg_fTime = 0.0f;// Globaler Zeitzähler
Programminitialisierung • Konfigurationsdialog wird aufgerufen und das Ergebnis wird in g_Config gespeichert • Danach wird die tbDitrect3-Klasse initialisiert • Ein Texturemanagerobjekt wird erstellt um die beiden Texturen zu laden • InitWater() und InitSkyBox() werden als nächstes aufgerufen • Gitterprinzip: es wird ein Statischer IndexBuffer erstellt, der mir den zu dritt gruppierten Indizes gefüllt wird; Die Methode GetVertexIndex(DWORD x, DWORD y) findet den Index jedes Vertex indem sie die Reihe mit der Anzahl Vertizes pro Reihe multipliziert und die Spalte dazu addiert • InitWater(): Vertex- und Index-Buffer werden erstellt; IndexBuffer wird gefüllt; Texturen und Effekte werden aufgerufen
Die Animation • Einmal pro Frame geht das Programm alle Vertizes durch und generiert sie neu, abhängig von position und Zeit; drei Funktionen erwarten eine Positionsangabe und mit hilfe des Zeitzählers liefern die Vertexposition(besonders Höhe), Normalenvektor und Texturkoordinaten: PositionProc(tbVector3 v), NormalProc() und TextureProc() • UpdateWater() ruft diese Funktionen auf und füllt den VertexBuffer • Die Render-Funktion: der Z-Buffer wird ausgeschaltet
Modelldateien • Sie sollen flexibel und offen für die Zukunft sein • Flexibilität durch Chunks: jeder Chunk hat ein Header(Beschreibung), gefolgt von den Daten , die Nach Chunktyp interpretiert werden; die Größe der Daten ist im Header angegeben, was erlaubt das unrelevante Chunks einfach übersprungen werden • Struktur für ChunkHeader: • struct TRIBASE_API tbModelChunkHeader • { • tbModelChunkTypeChunkType;// Typ des Chunks • DWORDdwDataSize;// Größe der Daten des Chunks • ChunkType ist vom Typ enum; alle ChunkTypen beginnen mit TB_CT_
Vertexdaten-Chunk: TB_CT_MODEL_VERTICES • struct TRIBASE_API tbModelVerticesChunkHeader • { • DWORDdwFVF;// Vertexformat • DWORDdwVertexSize;// Größe eines einzelnen Vertex • DWORDdwNumVertices;// Anzahl der Vertizes • floatfBoundingSphereRadius;// Radius der umgebenden Kugel • tbVector3vBoundingBoxMin;// Minimumpunkt des umgebenden Quaders • tbVector3vBoundingBoxMax;// Maximumpunkt des umgebenden Quaders • Indexdaten-Chunk TB_CT_MODEL_INDICES • struct TRIBASE_API tbModelIndicesChunkHeader • { • D3DFORMATIndexFormat;// D3DFMT_INDEX16 oder D3DFMT_INDEX32 • DWORDdwIndexSize;// Größe eines Index • DWORDdwNumIndices;// Anzahl der Indizes
Effekt-Chunk TB_CT_MODEL_EFFECTS • struct TRIBASE_API tbModelEffectHeader • { • characName[256];// Name des Effekts • BOOLbAlphaBlended;// Mit Alpha-Blending rendern? • D3DPRIMITIVETYPEPrimitiveType;// Typ der Primitiven • DWORDdwStartIndex;// Wo mit dem Rendern anfangen? • DWORDdwNumPrimitives;// Wie viele Primitiven rendern? • DWORDdwMinIndex;// Kleinster verwendeter Index • DWORDdwNumVertices;// Größter Index - Kleinster Index + 1 • DWORDdwEffectCodeSize;// Größe des Effektquellcodes • };