das ist jetzt mein dritter Tag OpenGL. Gestern und vorgestern habe ich es so weit gebracht, dass ich einen Raum mit sechs Wänden und einem sich drehenden, transparenten Quader innendrin programmieren konnte. Das ganz mit vier verschiedenen Texturen. Das ist zwar ganz nett - aber in dem Stil komme ich sicher nicht zu größeren Projekten.
Deshalb wollte ich jetzt fragen, wie ihr es so gemacht habt, um euren Projekten Struktur zu geben. Ich denke dabei an eine Engine-Klasse, die das Initialisieren und vor allem das Rendering verwaltet. Sie soll außerdem Texturen laden / freigeben können und in einer Liste alle Objekte speichern, die gerendert werden sollen. Wenn ihr in dem Punkt etwas zu sagen habt (zum Beispiel, dass es zu langsam ist), schreibt es doch bitte.
Dann würde ich die Objekte gerne alle als Klassen verwalten. Das hätten den Vorteil, dass ich eine Mutterklasse definieren könnte, die lediglich die Rendermethode (virtuell) besitzt. Von dieser Klasse könnte ich alle meine Objektklassen ableiben, die natürlich noch Eigenschaften bekommen und die Rendermethode überschreiben. Dann könnte ich in der Liste der Engine einfach alle Objekte hinzufügen, und beim Rendering alle Rendermethoden aufrufen.
Aber ich weiß nicht, wie schnell das ist. Und vor allem: ob es zu viel Speicher braucht. Es wäre ja schön so, aber wenn so alles viel langsamer wird, ist es auch nicht so ganz das Richtige.
Wie macht ihr das denn? Ich denke, ihr zeichnet auch nicht eure Welten mit tausenden Vertices, die ihr von Hand in einer einzigen Rendermethode aufruft.
Ich hoffe, ihr findet Zeit für eine Antwort.
Schonmal Danke im Vorraus.
So wie du es beschrieben hast ist das doch schonmal ein guter Ansatz. Optimierungen kommen dann via Frustrum Culling und Octrees etc. o.Ä. hinzu.
Das hängt auch stark vom Umfang des Projektes ab.
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Also solange du nicht auf die Idee kommst, jedes Quad als einzelnes Objekt zu verwalten, kommst du denke ich garnicht so schlecht weg.
Nur von der Idee einer alles könnenden Engineklasse solltest du dich verabschieden. Das wird einfach zu umfangreich. Mach es eher so, dass du autarke Managerklassen für Texturen, Sound, Modelle, Dateisystemzugriff, Partikel und so weiter hast, die du dann in deiner Hauptklasse, die dann auch die Spiellogik implementiert (wenn du die nicht auch an einen Manager abgibst), instanzierst und nutzt.
So mache ich das gerade bei meinem Strategy Game und fahre damit ganz gut, zumal ich auch mal eben schnell eine andere Anwendung mit diesen Klassen erzeugen kann.
Wenn du gerade anfängst kann ich nebenbei ja mal für Freepascal, Lazarus und SDL "werben". Freepascal ist eine Pascallsche Sprache, die fast genauso ist wie Delphi (nur besser ) und vorallem Plattformunabhängig. Das heißt, du kannst deine Projekte auch unter Linux oder MacOS kompilieren und nutzen. Allerdings ist es alles andere als Einfach, Delphiprojekte nach FreePascal zu konvertieren, wenn du nicht von anfang an darauf aufgebaut hast. Lazarus ist die graphische IDE zu FreePascal, die auch eine eigene Component Library, die LCL (das FPC-Gegenstück zur VCL) bietet. Damit lässt sich vorallem seit dem letzten Update FreePascal Code ganz gut bearbeiten. Und zu guter letzt SDL, welches du bei solchen Projekten als Fenster- und Eventverwaltung nehmen solltest. Damit fährt man was Plattformunabhängigkeit betrifft einfach am besten.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
So wie du es beschrieben hast ist das doch schonmal ein guter Ansatz.
Das klingt ja ganz gut.
Seth hat geschrieben:
Optimierungen kommen dann via Frustrum Culling und Octrees etc. o.Ä. hinzu.
Das ist mir klar, irgendwann werde ich mich damit beschäftigen müssen. Aber das ist dann etwas anderes - jetzt muss ich mir erst mal eine Ordnung in das Gesamte bringen.
Seth hat geschrieben:
Das hängt auch stark vom Umfang des Projektes ab.
Ich will meine Verwaltung eigentlich von Anfang an sauber programmieren. Denn: Am Anfang ist der Umfang noch klein, am Ende jedoch groß. Deswegen kann ich nicht einfach mal "schlecht" anfangen.
Lord Horazont hat geschrieben:
Also solange du nicht auf die Idee kommst, jedes Quad als einzelnes Objekt zu verwalten, kommst du denke ich garnicht so schlecht weg.
Jedes Quad will ich natürlich nicht als Objekt verwalten, ich hatte eher die Idee, Klassen zu erstellen, die mit Quadern, Säulen, Treppen, Räumen, Terrains, Schwimmbecken usw. klar kommen. Ich werden dann versuchen, immer die Klasse zu nehmen, die am meisten auf einmal erledigt. Ist das so noch im Rahmen? Ein Raum besteht zum Beispiel aus zwei Quadern - einer stellt die Innenseite dar, und der andere die Ansicht von außen.
Lord Horazont hat geschrieben:
Nur von der Idee einer alles könnenden Engineklasse solltest du dich verabschieden. Das wird einfach zu umfangreich. Mach es eher so, dass du autarke Managerklassen für Texturen, Sound, Modelle, Dateisystemzugriff, Partikel und so weiter hast, die du dann in deiner Hauptklasse, die dann auch die Spiellogik implementiert (wenn du die nicht auch an einen Manager abgibst), instanzierst und nutzt.
Meinst du wirklich? Ich hätte das so gemacht:
Code:
Engine
Textures (Texturen laden, freigeben, verwalten)
Sound (Kenn ich noch nicht; Verwaltung !?)
World (Eigentlicher Inhalt)
LoadFromFile (Welt laden)
Objects (Objekte verwalten)
Jedes Unterobjekt kann dann auf die Engine zugreifen, um z.B. Fehler zu melden. Natürlich alles in einer Unit - die Verwaltung ist so sehr viel einfacher.
Lord Horazont hat geschrieben:
Wenn du gerade anfängst kann ich nebenbei ja mal für Freepascal, Lazarus und SDL "werben".
Das klingt zwar ganz gut, aber der Haken an der Sache ist der: Ich will eigentlich erst mal "nur" eine 3D-Engine schreiben. Plattformunabhängigkeit verlangt noch einiges mehr - außerdem habe ich keine zweiten Rechner, um Linux o.Ä. zu installieren. Und drittens keine Lust
So im Großen und Ganzen habe ich die Antworten so aufgefasst, dass ich so anfangen kann. Natürlich freue ich mich immer noch über Antworten
Registriert: Do Sep 02, 2004 19:42 Beiträge: 4158
Programmiersprache: FreePascal, C++
Yogu hat geschrieben:
Lord Horazont hat geschrieben:
Wenn du gerade anfängst kann ich nebenbei ja mal für Freepascal, Lazarus und SDL "werben".
Das klingt zwar ganz gut, aber der Haken an der Sache ist der: Ich will eigentlich erst mal "nur" eine 3D-Engine schreiben. Plattformunabhängigkeit verlangt noch einiges mehr - außerdem habe ich keine zweiten Rechner, um Linux o.Ä. zu installieren. Und drittens keine Lust
Eben nicht. Du brauchst kein Linux für die Aktion (theoretisch zumindest). Umstellungen hast du eigentlich nur von der Grafischen Oberfläche her und ok, du musst SDL benutzen anstatt der VCL, aber die Tutorials sollten da doch sowieso schon ansetzen. Und wie du selbst sagst, du willst von anfang an "richtig" anfangen, und ein Schritt richtung Plattformunabhängigkeit ist immer einer in die richtige Richtung.
Wegen den Unterklassen: Das mag zwar am anfang so ok sein, aber je komplexer die einzelnen Dinge werden desto schwieriger wird es, später fehler im Code zu finden und zu beseitigen. Wenn die einzelnen Klassen klar getrennte Aufgabengebiete haben, dann kann man einen Fehler oft viel genauer Lokalisieren. Wenn du alles in einer klasse (und so zwangsläufig auch in einer Unit) hast, hast du irgendwann eine 1 MB große Unit mit 20000 Zeilen, wo du garnicht mehr richtig durchsteigst. Wenn du die Manager dagegen klar auftrennst hast du meinetwegen 10 oder 20 verschiedene Units, die alle einen klaren Aufgabenbereich haben, der sich nicht überschneidet, außer vielleicht, dass eine Klasse mehrere andere in sich vereint, z.B. ein Worldmanager, der eine Instanz vom Textur und eine vom Soundmanager hält.
Für Sound würde ich dir übrigens das für Freeware-Projekte kostenlose FMOD empfehlen (http://www.fmod.org), eine sehr umfangreiche und gut skalierbare Soundbibliothek, für die es Pascal-Header gleich mitgeliefert gibt.
Gruß Lord Horazont
_________________ If you find any deadlinks, please send me a notification – Wenn du tote Links findest, sende mir eine Benachrichtigung. current projects: ManiacLab; aioxmpp zombofant network • my photostream „Writing code is like writing poetry“ - source unknown
„Give a man a fish, and you feed him for a day. Teach a man to fish and you feed him for a lifetime. “ ~ A Chinese Proverb
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Also mein übliches Maß liegt ungefähr bei 2000 bis 3000 Zeilen für große Units (es gibt auch Außnahmen, aber mehr als 10.000 Zeilen hab ich noch nicht in eine Unit geklatcht).
Meine Engine besteht aus ca. 130 Dateien (*.pas und *.inc) und ca. 3,7 MB an Quelltext - und wenn ich das alles in einer Datei hätte, naja, das ist nicht mehr möglich. 130 Dateien sind zwar schon sehr viel, aber ich arbeite auch seit 2 Jahren dann.
Insgesamt hab ich das ganze über Events und über Klassenvererbung gelöst.
z.B. so:
Mein Object "TEngine" ist eine Kombination aus mehreren Klassen. Angefangen von "TEngine_Base", die sich NUR um das Laden und Freigeben des Levels kümmert. Dann kommt "TEngine_Render = class(TEngine_Base)", der sich nur um das Rendern kümmert. Das geht so weiter über Kollisionen bis hin zum finalen, multiplayerfähigem "TEngine"-Objekt.
Das alles ist in verschiedene Dateien gekapselt.
Wenn ihr da so aus Erfahrung sprecht, werde ich natürlich versuchen, mehrere Units zu bekommen. Aber ich werde wohl leider nicht drumrum kommen, erst zu viel in eine Unit zu packen, und genau diesen Fehler erst später zu merken. Es ist komisch, so in die Zukunft zu denken, aber mir ist der Sinn noch nicht so ganz klar. Ich denke, das muss ich durchmachen, und später dann merken, dass es falsch war. Anders klappt das wohl nicht.
Also: Ich mache eine Unit "GLEngine", die sich um das Allgemeine kümmert (die wird später sicher mal zu groß). Die ruft zum Rendern der Objekte einfach eine Methode "Render" der Klasse "TGLObject" auf, die da noch leer ist. In anderen Units werde ich die eigentlichen Objekte definieren und ihnen Rendermethoden geben. Die sind von der Mutterklasse der Objekte abgeleitet und können deshalb in der Engine-Unit ohne Einbinden der Objekt-Units gerendert werden. Sound kann ich noch nicht - dazu brauch ich noch Tutorials. Und Partikel erst recht nicht - die kommen erst sehr viel später.
Texturen kann ich ja ganz unabhängig von anderen Units vewalten - die können auf jeden Fall in eine eigene Unit. Und die Dateiverwaltung werde ich später implementieren, wahrscheinlich mit einer Urunit, die die Streams verwaltet. Genaueres werde ich wohl in den passenden Units einbinden. Aber das ist auch noch eine weilchen hin...
Registriert: Do Sep 25, 2003 15:56 Beiträge: 7810 Wohnort: Sachsen - ERZ / C
Programmiersprache: Java (, Pascal)
Als ich zu Java gewechselt bin hab ich mich auch gewundert was das soll. Für jede Klasse eine eigene Datei? Konnt ich mir nicht so recht erklären. Mittlerweile finde ich das viieeel übersichtlicher.
_________________ Blog: kevin-fleischer.de und fbaingermany.com
jetzt arbeite ich schon ein paar Stunden an der Engine, und das Resultat: 5 Units. GLBase, GLEngine, GLOptions, GLWorld und GLBaseObjects. In jedem außer GLBase steckt genau eine Klasse drin. (GLBase hat nur ein paar kleine Definitionen, darunter Records, kleine Klassen und ein paar Funktionen.) Erstaunlich, wie schnell ich meine Meinung geändert habe
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Lord Horazont hat geschrieben:
Also solange du nicht auf die Idee kommst, jedes Quad als einzelnes Objekt zu verwalten, kommst du denke ich garnicht so schlecht weg.
Auf die Idee bin ich aber gekommen . Ne ernsthaft - in meiner Engine gibt es es gibt für jedes Render-Objekt - und sei es auch noch so klein - eine Klasse. Doch die Geschwindigkeit leidet darunter eher nicht, da die meisten Objekte am Anfang in eine Display-Liste geschrieben werden. Und Sachen wie Kollisionsdetection übernehmen andere Klassen, die dann z.B. mehrere Quads zu einem Würfel zusammenfassen.
Das hat auch einen gewaltigen Vorteil. Stell dir vor, du willst zwei Würfel rendern. Jede Seite soll eine unterschiedliche Textur und einen eigenen Shader haben. Zudem ist beim ersten Würfel die untere Fläche nicht sichtbar. Beim zweiten Würfel willst du jetzt nicht, dass alle Seiten nach außen sondern nach innen zeigen. Beide Würfel sind um verschiedene Achsen und unter verschiedenen Winkeln gedreht. Die Frontseite des Würfels soll halb-transparent sein. Auch die Größen der beiden Würfel sind unterschiedlich usw ...
Wenn du all das jetzt in EINE Klasse names TWuerfel unterbringen willst, wirst du garantiert scheitern. Spätestens wenn du dann geskriptete Ereignisse hast, wie z.B. dass der Würfel wie ein leerer Pappkarton zusammenfällt oder explodiert, hast du mit einem TWuerfel ohne Quads als Objekte keine Chance mehr.
Bei einem Terrain sieht die Sache schon wieder anders aus. Aber bei einem Terrain kommt es z.B. seltener vor, dass jedes einzelne Quad einen anderen Shader hat. Man muss die Gruppierungen an die Komplexität UND an die Variabilität des Objekts anpassen.
Aber back 2 topic. Was du dir auf jeden Fall überlegen musst ist: Was soll meine Engine darstellen können. Wie flexibel soll die Engine sein. Was soll fix sein, was soll variabel gestalltet werden können. Also wenn du eine Engine programmieren willst, die GTA4, Tetris und Doom3 darstellen soll, bist du über das Ziel hinausgeschossen.
(ungefähr so:
Code:
case EngineMode of
emGTA4 : RenderAsGTA4;
emTetris : RenderAsTetris;
emDoom3 : RenderAsDoom3
end;
)
Ich hab bei meiner Engine mit einem Editor angefangen. Dann hab ich immer im Zickzackkurs erst die Engine und dann gleich den Editor erweitert. Somit konnte ich die neuen Sachen, die ich gerade eingebaut habe, sofort ausprobieren. Erst viel später, als ich bereits einfache Levels erstellen und speichern konnte, hab ich mich an ein Hauptprogramm gesetzt. Auch Post-Scene-Effects sind dann erst später im Programm dazugekommen (wer braucht das schon im Editor)
littleDave, du hast dich doch beim mir eingehackt und mir beim Coden zugeschaut!
Ich bin gerade auf genau dieses Problem gestoßen. Zuerst hatte ich eine Klasse TGLCube, die einen Würfel darstellt. Die Rendermethode ist so groß, weil ich für jede Fläche ein Record von TGLFace habe, und diese Eisntellungen setzen muss:
Aber ich denke, ich fasse eine Seite lieber in einer Prozedur zusammen. Der kann ich dann den Face-Record übergeben, sowie ein Array of TGLVector. Wie machst du das denn mit Objekten? Hast du dann bei einer Würfel-Klasse sechs Instanzen einer Quad-Klasse?
Registriert: Mi Mär 09, 2005 15:54 Beiträge: 372 Wohnort: München
Programmiersprache: Delphi, C#, FPC
Das ganze ist bei mir so aufgebaut:
Als erstes habe ich eine Basisklasse, TLevel_Root. Zudem habe ich eine Klasse, die eine Liste der Children jedes Objektes speichert, TLevel_ObjectList. Dann gibt es zwei grundlegende Typen von Klassen, einmal TLevel_Object und einmal TLevel_Polygon - beide abgeleiten von TLevel_Root.
Die Klassen, die von TLevel_Objekt abgeleitet sind, kümmern sich um die Positionierung und um die Rotation von Objekten. Die Klassen, die von TLevel_Polygon abgeleitet sind, kümmern sich um das Rendern.
Bei einem Würfel ist es z.B. so:
Ich habe ein Klasse TLevel_Cube = class(TLevel_Object). Diese Klasse kümmert sich um die Positionierung und um die Kollision. Da ja jedes Objekt von TLevel_Root abgeleitet ist und TLevel_Root die Liste TLevel_ObjectList hat, hat nun TLevel_Object sechs TLevel_Planes in der TLevel_ObjectList. TLevel_Plane ist abgeleitet von TLevel_Polygon.
Code:
TLevel_Cube
|_ TLevel_Plane (Hinten)
|_ TLevel_Plane (Rechts)
|_ TLevel_Plane (Vorne)
|_ TLevel_Plane (Links)
|_ TLevel_Plane (Oben)
|_ TLevel_Plane (Unten)
TLevel_Cube hat z.B. eine Variable, die sagt, ob das Quad nach innen oder nach außen Zeigen soll. Die einzelnen Flächen richtet es dann dementsprechend aus.
Das mit der Procedure für eine Fläche ist schon ein guter Ansatz, doch bei mir würde dein TGLPlane extrem groß sein. Daher hab ich mich für Klassen entschieden. Hab mal in den Anhang einen Screenshot aus dem Editor hinzugefügt. Es zeigt ein paar Render-Optionen, die EIN TLevel_Plane unterstützt.
Die Liste ist nicht komplett, mehr hat bei mir auf den Desktop einfach nicht draufgepasst.
Ach noch was, bevor ich es wieder vergesse - übernimm dich nicht, sonst wirst du nur enttäuscht. Gib dir kleine Aufgaben, die du auch wirklich schaffen kannst. Dann wächst du langsam mit der Herausforderung. Es ist noch kein Meister vom Himmel gefallen - und niemand erwartet von dir, dass du nach 2 Wochen Crysis nachprogrammieren kannst - also erwarte es auch nicht von dir selbst.
Du hast keine ausreichende Berechtigung, um die Dateianhänge dieses Beitrags anzusehen.
Stell dir vor, du willst zwei Würfel rendern. Jede Seite soll eine unterschiedliche Textur und einen eigenen Shader haben. Zudem ist beim ersten Würfel die untere Fläche nicht sichtbar. Beim zweiten Würfel willst du jetzt nicht, dass alle Seiten nach außen sondern nach innen zeigen. Beide Würfel sind um verschiedene Achsen und unter verschiedenen Winkeln gedreht. Die Frontseite des Würfels soll halb-transparent sein. Auch die Größen der beiden Würfel sind unterschiedlich usw ...
Ein Objekt für Modelle würde es auch tun, denn selbiges würde einfach entsprechende Baugruppen verwalten und könnte diese entsprechend ansteuern. So hab lief das bei mir bisher jedenfalls immer ab.
Die Basisklasse aller Objekte enthält eine Methode Render, die die Matrix pusht, die drei Vektoren (Origin, Rotation, Scale) auf die Matrix anwendet, die eigentliche Rendermethode aufruft, die virtuell deklariert ist, und die Matrix wieder poppt. Die einzelnen Objekte müssen dann nur noch sich selber rendern. Dazu wird für jede Seite zuerst SetFace aufrerufen, welche Einstellungen für die Oberfläche setzt (dafür habe ich einen Record, der die Daten wie Textur und später auch Material, Licht usw. speichert) und schließlich die OpenGL-Funktionen zum Senden der UVs und Vertices.
Ein Würfel hat noch die Eigenschaft Inverted, welche angibt, ob der Würfel von innen sichtbar sein soll. Diese Eigenschaft mache ich mir in TGLHollowCube zu Nutzen, welches einen holen Würfel rendert. Dieser hat eine Außenseite (TGLCube) und eine Innenseite (auch TGLCube). Auf die Unterobjekte kann ich jederzeit zugreifen und sie verändern, und beim Rendern werden einfach beide Objekte gerendert.
Registriert: Fr Dez 28, 2007 20:24 Beiträge: 62 Wohnort: Berlin
Ich habe alles in diesem Thread mit interesse gelesen doch sind mir jetzt noch einige Fragen offen:
Was genau definiert eine Engine? - Ist es eine Sammlung von procedures die Objekte hinzufügen,darstellen, bewegen, verändern können?
Womit fängt man an Quelltext mäßig? - Erstellt man einen Idlehandler und ruft dort alle anderen Funktionen und Prozeduren auf?
Wann weis welche prozedur was sie mit welchem objeckt machen soll ?
Wie wird denn alles zum schluss in einer Renderprocedure zusammengefasst?
nach dem motto von 1 bis anzahl zeichne Objekt von I oder wie?
Mitglieder in diesem Forum: 0 Mitglieder und 6 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.