Die Community zu .NET und Classic VB.
Menü

Vorerstellte Objekte laden

 von 

Einführung  

Bis jetzt haben wir sämtliche Geometrie, die wir brauchten manuell erstellt - Vierecke und Würfel meistens. Sie haben vielleicht noch nicht darüber nachgedacht, wie kommerzielle/richtige Spiele funktionieren wenn es zu (komplexen) 3D-Modellen kommt. Man muss auf jeden Fall kein Genie sein um zu erkennen, dass diese Objekte nicht per Code eingetippt wurden, was Tausende von Zeilen für einen Frame bräuchte, also ein nicht sehr cleverer Weg ist.

Stattdessen laden wir Objekte, die von einem anderen Programm erstellt wurden, normalerweise ein 3D-Modeller. Wir lesen die Datei und laden die benötigten Texturen und Materialien. Es gibt auch noch andere Vorteile als nur die Einfachheit - Sie können die Datei nach der finalen Kompilierung des Programms noch verändern. Wenn Sie sich dafür entscheiden eine geringere Vertexanzahl zu benutzen, dann können Sie die Datei noch öffnen, ein bisschen verändern und dann wieder speichern - und die Änderungen werden sich gleich auf das Spiel ausüben. Sie können z.B. auch verschiedene Versionen eines Modells haben, welche mit geringem, durchschnittlichen und hohem Detail, so dass der Benutzer das Spiel sowohl auf einem Low-End als auch auf einem modernen PC spielen kann.

Von jetzt an werden wir also immer externe Objekte benutzen, anstatt sie im Code zu definieren. Und weiter geht's...

3D Objekte erstellen  

Das erste, was Sie für diese Lektion brauchen ist eine Grundlagenwissen wie 3D-Modelle funktionieren - und das sollten Sie nach den letzten Kapiteln auch eigentlich haben. Aber wenn Sie immer noch nicht alles verstanden haben, dann sollten Sie sich mal eines der 3D-Modelierungs-Tutorien im Internet durchlesen(Es würde hier den Rahmen sprengen).

Zweitens brauchen Sie irgendeine Software. Es gibt Hunderte von billigen/kostenlosen 3D-Modellierungsprogrammen, wenn Sie es aber mit dem Erstellen von guten 3D-Modellen ernst meinen, dann sollten Sie sich mal eines der professionellen Pakete anschauen(wie z.B. 3D Studio Max oder Lightwave). Wenn Sie ein ganz schlechter 3D-Modeller sind, dann können Sie auch nach einem Künstler suchen, der das kann - und hoffen, dass er die richtige Software hat.

Drittens brauchen Sie einen Konverter. Direct3D benutzt sein eigenes Format - es gibt einen Konverter im DirectX SDK, der .3DS-Dateien in .X-Dateien umwandelt( Es wqürde wahrscheinlich gegen die EULA-Vereinbarung verstoßen, wenn ich ihn hier hochladen würde).

Obwohl es noch einige gibt, die Sie beachten müssen, ist der letzte wirklich wichtige Punkt für einen Programierer die Modell-Komplexität. Basierend auf dem Design Ihrer Engine sollten Sie irgendwo eine Grenze für Modelle ziehen. Wenn Ihr Spiel zum Beispiel auf einem durchnittlichen Rechner laufen soll(Low-end 3D-Karte und passabeler Prozessor), dann werden Sie die Dreieckanzahl so ca. um 3000 halten wollen; und wenn die Landschaft schon 1500 Dreiecke verbraucht, dann dürfen sämtliche anderen Objekte nicht mehr als 1500 Dreiecke einnehmen.

Vorerstellte Objekte laden  

Zum Glück sind die Grundlagen des 3D-Modell-Ladens in Direct3D sehr einfach; erst wenn es zu Animation und Skinning kommt, wird es kompliziert. Wie oben schon gesagt brauchen wir ein Objekt - wie Sie es im Beispiel sehen können. Es ist nur eine Kugel mit einem rotierenden "DirectX 4 VB". Ich habe es mit 3DSMax 2 gemacht und habe dann den Konverter aus dem SDK(Conv3DS.exe) benutzt um es in eine X-Datei zu verwandeln. Wenn Sie dieses Programm haben, dann sollten Sie es so benutzen: "Conv3DS -m -V2 file.3ds". Der -V2-Teil ist optional und bewirkt, dass einfache Informationen ausgegeben werden. Sie können natürlich auch noch andere Parameter hinzufügen, Sie müssen nur daran denken, dass -m drinzulassen.

Jetzt können wir anfangen mit unserem Modell rumzuspielen. Ich werden einen Teil des Kapitels "Einführung in Lighting" umschreiben, so dass es Modelle rendert - teilweise, weil die Lichter, usw. dort schon aufgestellt sind. Es ist außerdem viel einfacher die Auswirkung des Lightings in diesem Beispiel zu sehen, als in dem eigentlichen Beispiel(weil wir eine komplexere Geometrie haben). Hier ist der neue Code für den Deklarationsbereich. Jetzt müssen wir übrigens auch nicht mehr mit benutzerdefinierten Vertexformaten rumspielen:

'###########
'## MESH  ##
'###########
 Dim Mesh As D3DXMesh

'##############################
'## TEXTUREN AND MATERIALIEN ##
'##############################
 Dim MeshMaterials() As D3DMATERIAL8
    ' Mesh Materialien Daten
 Dim MeshTextures() As Direct3DTexture8
    ' Mesh Texturen
 Dim nMaterials As Long
    ' Wie viel Materialien/Texturen wir haben

Das Mesh Objekt soll einmal alle unsere Informationen halten. Außerdem werden wir wieder die D3DX-Klasse als Hilfe benutzen. Dann haben wir noch die Materialien und Texturen - beides nach oben offene Arrays. Nachdem wir unser Modell in den Speicher geladen haben, werden wir abfragen, wie viele Texturen und Materialien vorhanden sind und vergrößern diese Arrays dann nach der Anzahl.

Der nächste Codeteil ist komplett neugeschriebener Code für unsere "InitGeometry"-Funktion. Alles außer die Fehlerbehandlung wurde verändert - also hier kommt er:

Private Function InitialiseGeometry() As Boolean
  On Error Goto BailOut: '//Fehler Handler starten
  '//0. Die benötigten Variablen:

  Dim mtrlBuffer As D3DXBuffer
    '//Speichert ein paar wichtige Daten für uns
  Dim I As Long
    'Eine Schleifenvariable
  Dim TextureFile As String
     'eine Textur
'//1. die Daten aus der Datei laden
  Set Mesh = D3DX.LoadMeshFromX(App.Path & _
    "\lesson08.x", D3DXMESH_MANAGED, _
    D3DDevice, Nothing, mtrlBuffer, nMaterials)

  If Mesh Is Nothing Then Goto BailOut:
    '//Abbruch, wenn der vorige Aufruf schief gegangen ist.
'//2. Platz für Texturen und Meterialien machen
  ReDim MeshMaterials(nMaterials) As D3DMATERIAL8
  ReDim MeshTextures(nMaterials) As Direct3DTexture8
'//3. Jetzt werden die Arrays mit den benötigten
'       Informationen gefüllt
  For I = 0 To nMaterials - 1
    '//Nun kopieren wir die Daten, die wir geladen haben
    '   in unsere Struktur
    D3DX.BufferGetMaterial mtrlBuffer, I, MeshMaterials(I)
      '//eine Lücke füllen - die Ambient-Eigenschaften
    MeshMaterials(I).Ambient = MeshMaterials(I).diffuse
      '//Name der benutzten Textur für diesen Teil erfahren
    TextureFile = D3DX.BufferGetTextureName(mtrlBuffer, I)
      '//Jetzt wir die textur geladen

    If TextureFile <> "" Then
      'Ja nicht Textur aus einem leeren String laden
      Set MeshTextures(I) = _
        D3DX.CreateTextureFromFileEx(_
        D3DDevice, App.Path & "\" & TextureFile, _
        128, 128, D3DX_DEFAULT, _
        0, D3DFMT_UNKNOWN, D3DPOOL_MANAGED, D3DX_FILTER_LINEAR, _
        D3DX_FILTER_LINEAR, 0, ByVal 0, ByVal 0)
    End If
  Next I
'//4. Gibt ein paar unbenötigte Extra-Informationen aus
  Debug.Print "Number of Faces in mesh: " & _
    Mesh.GetNumFaces
  Debug.Print "Number of Vertices in mesh: " & _
    Mesh.GetNumVertices

  InitialiseGeometry = TrueExit Function
BailOut:
  Debug.Print "Error occured in InitGeometry() Code"
  InitialiseGeometry = False
End Function

Der Code erklärt sich fast von selbst - und die D3DX-Bibliothek macht das Meiste der harten Arbeit wir können uns beruhigt zurücklehnen. Der letzt Teil von 3 könnte noch ein bisschen an Ihre Bedürfnisse angepasst werden - dieses Beispiel nutzt nämlich CreateTextureFromFileEx. Sie könnten es leicht dazubringen verschiedene Transparenzen und Größen zu benutzen, oder den Aufruf durch den alten CreateTextureFromFile zu ersetzen - wenn Sie z.B. nichts über die Textur wissen, oder es Sie einfach nicht interessiert.

An diesem Punkt haben wir ein gültiges Objekt im Speicher, mit dem wir jetzt rumspielen können.

Das Objekt rendern  

Das Objekt jetzt zu rendern ist auch nicht viel schwerer, obwohl Sie immer noch alle Matrizen und Transformationen managen müssen, die sie wollen. Interessant ist es auch zu sehen, wie die verschiedenen Lichttypen die Framerate beeinflussen: wie ich schon in der letzten Lektion gesagt habe, die verschiedenen Lichttypen haben unterschiedliche Berechnungskosten. Das Ganze war schwer zu sehen, wenn wir nur acht Dreiecke hatten, aber jetzt, wo es Hunderte sind, ist der Unterschiedrecht groß. Als ein Beispiel: auf meinem Computer bekomme ich 170fps bei Directional Light, 160fps bei Point Light und 145fps bei Spot Light - ein deutlicher Abwärtstrend.

Wie Sie hoffentlich bemerkt haben, habe wir das Objekt in Teilen geladen - wir haben eine Zahl Texturen und Materialien und zwar für jeden Teil eine Textur- /Materialkombination. Weil wir diese Teile in einer Schleife rendern werden, ist es durchaus möglich die Teile mit unterschiedlicher Transformation zu rendern - wenn sie wissen, welche teil welcher ist(das geht nach dem Trial & Error Verfahren - Sie könnten natürlich auch ein eigenes Dateiformat dafür schreiben). Es wäre eventuell sogar möglich sämtliche Objekte in einer großen Datei zu haben - wenn Sie wissen in welcher Reihenfolge Sie kommen - es macht die Datei aber auch ein wenig schwierig für das Editieren nachher.

For I = 0 To nMaterials - 1
  '//Setup the renderer for this part of the mesh
  D3DDevice.SetMaterial MeshMaterials(I)
  D3DDevice.SetTexture 0, MeshTextures(I)
  '//Draw the current part of the mesh
  Mesh.DrawSubset I
Next I

Sehr einfach. Merken Sie sich einfach, dass dieser Teil zwischen Device.BeginScene und Device.EndScene kommen muss.

Beispielprojekt zum Tutorial 

Ihre Meinung  

Falls Sie Fragen zu diesem Tutorial haben oder Ihre Erfahrung mit anderen Nutzern austauschen möchten, dann teilen Sie uns diese bitte in einem der unten vorhandenen Themen oder über einen neuen Beitrag mit. Hierzu können sie einfach einen Beitrag in einem zum Thema passenden Forum anlegen, welcher automatisch mit dieser Seite verknüpft wird.