opengl Engine vom Programmierneuling! oder: kann man das so machen?
-
Hi erstmal !
Nachdem ich in den letzten Wochen etwas mit Direct X und OpenGl rumexperimentiert hatte und auch Engines wie Irrlicht und Ogre mal getestet hatte wollte ich die WinApi und den ganzen OpenGL kram in Klassen auslagern. Also sozusagen ne kleine Engine entwerfen. Als Ausgangsprojekt habe ich ein Tutorial von "NeHe" genommen und das dann in eine Klasse gepackt. Die "Engine" sollte also das Fenster erstellen und eine Pyramide und einen Würfel darstellen können. Das Auslagern der Funktionen war anfangs gar nicht so schwer, erst mit der WndProc begann es kompliziert zu werden. Nach jeder menge Kaffee und mindestens genausovielen Tipps aus dem Internet hatte ich es endlich geschafft! Da ich aber anfänger bin und von OOP nicht allzuviel verstehe (deshalb mache ich das. Will OOP verstehen!!!) habe ich jetzt keine Ahnung ob das so wie ich es geschrieben habe "guter Stil" ist. Oder anders gesagt so einfach wie ich es geschrieben habe kann das nicht wirklich guter stil sein . Bevor ich jetzt anfange meine "Engine" auszubauen wollte ich mir hier rat suchen. Ich Poste mal meine klasse und hoffe mir kann jemand tipps geben wie es "besser" geht. Auch über Erklärungen weshalb man etwas so und nicht so macht wären echt klasse, bin absoluter Programmieranfänger und hatte bis vor 2 Wochen nur C++/CLI genutzt. Gerade was also die WinApi anbelangt bin ich also nicht allzu fit. So, hier erstmal der Code://klassen.h #include <Windows.h> #include <gl\GL.h> #include <gl\GLU.h> class ladeObjekt { //Funktionen für die Erstellung von 3D-Objekten public : static void ladePyramide(void); static void ladeWuerfel(void); }; class ogl { //------------------------------------------------------------------------------------- private: HINSTANCE hInstance; HWND hWnd; HDC hDC; HGLRC hRC; public: ogl(HINSTANCE hInstance); virtual ~ogl(); static LRESULT CALLBACK WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); bool handleMessages(); static GLvoid ReSizeGLScene(GLsizei width, GLsizei height); // verändert die Größe und initialisiert das GL-Fenster static GLvoid KillGLWindow(bool fullscreen, HGLRC hRC,HDC hDC, HWND hWnd, HINSTANCE hInstance ); // Entferne das Fenster korrekt static int InitGL(GLvoid); bool createWindow(char* title, int width, int height, int bits, bool fullscreenflag); static int DrawGLScene(GLfloat rtri, GLfloat rquad );// Hier kommt der ganze Zeichnen-Kram hin void swap(); //--------------------------------------------------------------------------------------- };
//klassen.cpp #include "resource.h" #include "klassen.h" //----------------------------------------------------------------------------------------------------------- // //Farben und Vertices der Pyramide //----------------------------------------------------------------------------------------------------------- void ladeObjekt::ladePyramide(void) { //Hier sind nur vertices und farben drin also eher uninteressant... .... } //----------------------------------------------------------------------------------------------------------- // //Farben und Vertices des Würfels //----------------------------------------------------------------------------------------------------------- void ladeObjekt::ladeWuerfel(void) { //Hier sind nur vertices und farben drin also eher uninteressant... .... // } //----------------------------------------------------------------------------------------------------------- // //Konstruktor //----------------------------------------------------------------------------------------------------------- ogl::ogl(HINSTANCE hInstance) { this->hInstance = hInstance; hWnd = NULL; hDC = NULL; hRC = NULL; } //----------------------------------------------------------------------------------------------------------- // //Destruktor //---------------------------------------------------------------------------------------------------------- ogl::~ogl() { if(hRC) { wglMakeCurrent(NULL, NULL); wglDeleteContext(hRC); } if(hWnd && hDC) { ReleaseDC(hWnd, hDC); } if(hWnd) { DestroyWindow(hWnd); } } //-------------------------------------------------------------------------------------------------------------- // //Window Process kümmert sich um Nachrichten wie Tastatur und Mausereignisse aber auch Systemereignisse //-------------------------------------------------------------------------------------------------------------- LRESULT CALLBACK ogl::WinProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch(msg) { case WM_SYSCOMMAND: switch (wParam) { //Screensaver unterdrücken case SC_SCREENSAVE: return 0; //Stand-By unterdrücken case SC_MONITORPOWER: return 0; } break; case WM_KEYDOWN: switch (wParam) { case VK_ESCAPE: PostQuitMessage(0); break; case VK_F1: ShowCursor(true); MessageBox(NULL,"Nachricht wurde von WndProc empfangen\nund verarbeitet. Test wird beendet!", "Programm wird beendet !",MB_OK|MB_ICONINFORMATION); PostQuitMessage(0); break; case WM_CLOSE: DestroyWindow(hWnd); break; case WM_DESTROY: PostQuitMessage(0); break; } default: return DefWindowProc(hWnd, msg, wParam, lParam); } return 0; } //----------------------------------------------------------------------------------------------------------------- // //halte Ausschau nach und behandle Nachrichten //----------------------------------------------------------------------------------------------------------------- bool ogl::handleMessages() { MSG msg = {0}; while(PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { if(msg.message == WM_QUIT) { return false; } TranslateMessage(&msg); DispatchMessage(&msg); } return true; } //------------------------------------------------------------------------------------------------------------------------ // //Ändere die Fenstergröße //------------------------------------------------------------------------------------------------------------------------ GLvoid ogl::ReSizeGLScene(GLsizei width, GLsizei height) // verändert die Größe und initialisiert das GL-Fenster { if (height==0) // Verhindere eine Division durch 0, indem { height=1; // die Höhe auf 1 gesetzt wird } glViewport(0,0,width,height); // Resette die aktuelle Ansicht (Viewport) glMatrixMode(GL_PROJECTION); // wähle die Projektions-Matrix aus glLoadIdentity(); // Resette die Projection Matrix // Calculate The Aspect Ratio Of The Window gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f); glMatrixMode(GL_MODELVIEW); // Wähle die Modelview Matrix aus glLoadIdentity(); // Resette die Modelview Matrix } //------------------------------------------------------------------------------------------------------------------------- // //Zerstöre das Fenster //------------------------------------------------------------------------------------------------------------------------- GLvoid ogl::KillGLWindow(bool fullscreen, HGLRC hRC, HDC hDC, HWND hWnd, HINSTANCE hInstance ) // Entferne das Fenster korrekt { if (fullscreen) // Sind wir im Fullscreen Modus? { ChangeDisplaySettings(NULL,0); // Wenn ja, wechsle zurück zum Desktop ShowCursor(TRUE); // Zeige den Maus-Zeiger } if (hRC) // Haben wir einen Rendering Context? { if (!wglMakeCurrent(NULL,NULL)) // Können wir den DC und RC Kontext freigeben? { MessageBox(NULL,"Freigabe des DC und RC Kontextes nicht gelungen!","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); } if (!wglDeleteContext(hRC)) // Können wir den RC löschen? { MessageBox(NULL,"Freigabe des Rendering Context nicht gelungen!","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); } hRC=NULL; // Setze RC auf NULL } if (hDC && !ReleaseDC(hWnd,hDC)) // Können wir DC freigeben? { MessageBox(NULL,"Freigabe des Device Context nicht gelungen.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hDC=NULL; // Setze DC auf NULL } if (hWnd && !DestroyWindow(hWnd)) // Können wir das Fenster zerstören? { MessageBox(NULL,"Konnte hWnd nicht zerstören.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hWnd=NULL; // Setze hWnd auf NULL } if (!UnregisterClass("OpenGL",hInstance)) // Können wir die Klasse de-registrieren? { MessageBox(NULL,"Konnte Klasse nicht de-registrieren","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION); hInstance=NULL; // Setze hInstance auf NULL } } //--------------------------------------------------------------------------------------------------------------- // //initialisiere OpenGl //--------------------------------------------------------------------------------------------------------------- int ogl::InitGL(GLvoid) // Der ganze Setup Kram für OpenGL kommt hier rein { glShadeModel(GL_SMOOTH); // aktiviert weiches Shading (Smooth Shading) glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Schwarzer Hintergrund glClearDepth(1.0f); // Depth Buffer Setup glEnable(GL_DEPTH_TEST); // aktiviert Depth Test glDepthFunc(GL_LEQUAL); // Die Art des auszuführenden Depth Test glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); // wirklich nette Perspektiven Berechnungen return TRUE; // Initialisierung war OK } //---------------------------------------------------------------------------------------------------------- // //erstelle das Fenster //---------------------------------------------------------------------------------------------------------- bool ogl::createWindow(char* title, int width, int height, int bits, bool fullscreenflag) { GLuint PixelFormat; // Enthält die Ergebnisse nachdem nach was passendem gesucht wurde WNDCLASS wc; // Fenster Klassen Struktur DWORD dwExStyle; // erweiterter Fenster-Stil DWORD dwStyle; // Fenster-Stil RECT WindowRect; // Enthält die obere linke / untere rechte Eckwerte des Rechtecks WindowRect.left=(long)0; // Setze linken Wert auf 0 WindowRect.right=(long)width; // Setze rechten Wert auf die gewünschte Breite WindowRect.top=(long)0; // Setze den oberen Wert auf 0 WindowRect.bottom=(long)height; // Setze unteren Wert auf die gewünschte Höhe bool fullscreen=fullscreenflag; // Setze das globale Fullscreen Flag hInstance = GetModuleHandle(NULL); // Ermittle die Instanz für unser Fenster wc.style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC; // Zeichne neu beim bewegen und eigener DC für's Fenster wc.lpfnWndProc = (WNDPROC) WinProc; // WndProc behandelt die Nachrichten wc.cbClsExtra = 0; // Keine extra Fenster-Daten wc.cbWndExtra = 0; // Keine extra Fenster-Daten wc.hInstance = hInstance; // Setze die Instanz wc.hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_MYICON)); // Lade mein eigenes Icon wc.hCursor = LoadCursor(NULL, IDC_ARROW); // Lade den Pfeil-Zeiger wc.hbrBackground = NULL; // es wird kein Hintergrund für GL benötigt wc.lpszMenuName = NULL; // Wir wollen kein Menü wc.lpszClassName = "OpenGL"; // Setze den Klassen-Namen if (!RegisterClass(&wc)) // Versuche die Fenster-Klasse zu registrieren { MessageBox(NULL,"Fehler bei der registrierung der Fenster-Klasse!","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // beende und gebe FALSE zurück } if (fullscreen) // Vollbild-Modus? { DEVMODE dmScreenSettings; // Device Modus memset(&dmScreenSettings,0,sizeof(dmScreenSettings)); // Stelle sicher, dass der Speicher geleert ist dmScreenSettings.dmSize=sizeof(dmScreenSettings); // Größe der Devmode Struktur dmScreenSettings.dmPelsWidth = width; // ausgewählte Screen Breite dmScreenSettings.dmPelsHeight = height; // ausgewählte Screen Höhe dmScreenSettings.dmBitsPerPel = bits; // ausgewählte Bits Per Pixel dmScreenSettings.dmFields=DM_BITSPERPEL|DM_PELSWIDTH|DM_PELSHEIGHT; // Versuche gewählten Modus zu setzen und Ergebnisse zurückliefern. ANMERKUNG: CDS_FULLSCREEN lässt die Start-Leiste nicht anzeigen. if (ChangeDisplaySettings(&dmScreenSettings,CDS_FULLSCREEN)!=DISP_CHANGE_SUCCESSFUL) { // Wenn der Modus fehl schlägt, biete zwei Optionen an. Beenden oder im Fenster-Modus laufen lassen. if (MessageBox(NULL," Der Vollbildmodus wird von Ihrer Videokarte nicht unterstützt. Benutzen sie bitte den Fenstermodus!","Achtung!",MB_YESNO|MB_ICONEXCLAMATION)==IDYES) { fullscreen=FALSE; // wähle Fenster-Modus aus (Fullscreen=FALSE) } else { // Message Box anzeigen, die den Benutzer wissen lässt, dass das Programm geschlossen wird. MessageBox(NULL,"Programm wird jetzt geschlossen.","Fehler",MB_OK|MB_ICONSTOP); return FALSE; // beende und gebe FALSE zurück } } } if (fullscreen) // Sind wir immer noch im Fullscreen Modus? { dwExStyle=WS_EX_APPWINDOW; // erweiterter Fenster-Stil dwStyle=WS_POPUP; // Fenster-Stil ShowCursor(FALSE); // verstecke Maus-Zeiger } else { dwExStyle=WS_EX_APPWINDOW | WS_EX_WINDOWEDGE; // erweiterter Fenster-Stil dwStyle=WS_OVERLAPPEDWINDOW; // Fenster-Stil } AdjustWindowRectEx(&WindowRect, dwStyle, FALSE, dwExStyle); // justiere das Fenster der angeforderten Größe entsprechend // Create The Window if (!(hWnd=CreateWindowEx( dwExStyle, // erweiterter Stil für das Fenster "OpenGL", // Klassen Name title, // Fenster Titel dwStyle | // ausgewählter Fenster Stil WS_CLIPSIBLINGS | // benötigter Fenster-Stil WS_CLIPCHILDREN, // benötigter Fenster-Stil 0, 0, // Fenster Position WindowRect.right-WindowRect.left, // berechne die justierte Fenster-Breite WindowRect.bottom-WindowRect.top, // berechne die justierte Fenster-Höhe NULL, // Kein Parent Fenster NULL, // Kein Menü hInstance, // Instanz NULL))) // Leite nichts an WM_CREATE weiter { ogl::KillGLWindow(fullscreenflag,hRC,hDC,hWnd,hInstance); // Resette die Ansicht MessageBox(NULL,"Fehler bei der Fenstererstellung.","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // beende und gebe FALSE zurück } //----------------------------------------------------------------------------------------------------------- // //Pixelformardescritor //----------------------------------------------------------------------------------------------------------- static PIXELFORMATDESCRIPTOR pfd= // pfd teilt Windows mit, wie wir die Dinge haben wollen { sizeof(PIXELFORMATDESCRIPTOR), // Größe des Pixel Format Descriptors 1, // Versions Nummer PFD_DRAW_TO_WINDOW | // Format muss Fenster unterstützen PFD_SUPPORT_OPENGL | // Format muss OpenGL unterstützen PFD_DOUBLEBUFFER, // Muss Double Buffering unterstützen PFD_TYPE_RGBA, // Fordere ein RGBA Format an bits, // wähle unsere Farbtiefe aus 0, 0, 0, 0, 0, 0, // Color Bits werden ignoriert 0, // Kein Alpha Buffer 0, // Shift Bit wird ignoriert 0, // Kein Accumulation Buffer 0, 0, 0, 0, // Accumulation Bits ignorieren 16, // 16Bit Z-Buffer (Depth Buffer) 0, // Kein Stencil Buffer 0, // Kein Auxiliary Buffer PFD_MAIN_PLANE, // Haupt-Zeichen-Schicht 0, // Reserviert 0, 0, 0 // Layer Masken werden ignoriert }; if (!(hDC=GetDC(hWnd))) //Haben wir einen Device Kontext erhalten? { ogl::KillGLWindow(fullscreenflag,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Konnte keinen GL Device Kontext erstellen.","Fehler!",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } if (!(PixelFormat=ChoosePixelFormat(hDC,&pfd))) // Hat Windows ein passendes Pixel Format gefunden? { ogl::KillGLWindow(fullscreenflag,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Kein passendes PixelFormat vorhanden.","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } if(!SetPixelFormat(hDC,PixelFormat,&pfd)) // Können wir das Pixel Format setzen? { ogl::KillGLWindow(fullscreenflag,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Kann PixelFormat nicht setzen.","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } if (!(hRC=wglCreateContext(hDC))) // Können wir einenRendering Kontext kriegen? { ogl::KillGLWindow(fullscreenflag,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Erhalte keinen GL Rendering Kontext.","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } if(!wglMakeCurrent(hDC,hRC)) // Versuche den Rendering Kontext zu aktivieren { ogl::KillGLWindow(fullscreen,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Kann den GL Rendering Kontext nicht erstellen.","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } ShowWindow(hWnd,SW_SHOW); // Zeige das Fenster SetForegroundWindow(hWnd); // Etwas höhere Priorität SetFocus(hWnd); // Setze den Tastatur-Fokus auf das Fenster ogl::ReSizeGLScene(width, height); // Initialisiere unseren perspektivischen GL-Screen if (!ogl::InitGL()) // Initialisiere unser neu erzeugtes GL Fenster { ogl::KillGLWindow(fullscreen,hRC,hDC,hWnd,hInstance); // Resette die Anzeige MessageBox(NULL,"Initialisation nicht gelungen .","Fehler",MB_OK|MB_ICONEXCLAMATION); return FALSE; // Gebe FALSE zurück } return TRUE; // Erfolg } //----------------------------------------------------------------------------------------------------------------- // //Zeichne die OpenGl Scene //------------------------------------------------------------------------------------------------------------------ int ogl::DrawGLScene(GLfloat rtri, GLfloat rquad ) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Lösche den Bildschirm und den Depth-Buffer glLoadIdentity(); // Resettet die aktuelle Modelview Matrix glTranslatef(-1.5f,0.0f,-6.0f); // Sicht ausrichten glRotatef(rtri,1.0f,1.0f,0.0f); // Dreieck um X und Y-Achse rotieren glBegin(GL_TRIANGLES); // Beginne Primitive Dreieck zu zeichnen ladeObjekt::ladePyramide();// Koordinaten und Farben der Pyramide laden glEnd(); // Fertig gezeichnet glLoadIdentity(); // Resette die aktuelle Modelview Matrix glTranslatef(1.5f,0.0f,-6.0f); // 1.5 Einheiten nach links und dann 6 Einheiten in den Bildschirm hinein glRotatef(rquad,1.0f,0.0f,1.0f); // Rotiere den Würfel auf der X und Z-Achse glBegin(GL_QUADS); // Beginne Primitive Rechteck zu zeichnen ladeObjekt::ladeWuerfel(); // Koordinaten und Farben des Würfels laden glEnd(); // Fertig gezeichnet return TRUE; // Alles war OK } //------------------------------------------------------------------------------------------------------------------ // //Wechsle zwischen den Puffern //------------------------------------------------------------------------------------------------------------------ void ogl::swap() { SwapBuffers(hDC); }
Recht langer Post geworden, gebe mir aber Mühe das in Zukunft kürzer zu halten.
Gruß littles
-
littles schrieb:
Da ich aber anfänger bin und von OOP nicht allzuviel verstehe (deshalb mache ich das. Will OOP verstehen!!!) habe ich jetzt keine Ahnung ob das so wie ich es geschrieben habe "guter Stil" ist.
Code wird nicht dadurch objektorientiert, wenn man alles in eine Klasse packt.
Als erstes könntest Du versuchen das Single responsibility principle anzuwenden. Deine ogl-Klasse übernimmt zu viele verschiedene Aufgaben.
http://en.wikipedia.org/wiki/SOLID_(object-oriented_design)
Nachtrag:
Und eine Engine ist was anderes. Ich warne Dich nur vor, weil hier manche Leute allergisch auf sowas reagieren
-
Danke für den link! Werde noch den GL kram und den WinApi kram trennen. Das ist jetzt sozusagen der erste Ansatz gewesen der sich Compilieren ließ und auch lief. Wieso muss ich eigentlich die ganzen statics einsetzen? ginge das, abgesehen von WinProc nicht auch ohne?
Naja, ne Engine ist das nicht, ganz klar, aber es soll als "Hello World" für den Start einer Engine dienen. Also sozusagen der Startpunkt um eine Engine aufzubauen. Daher auch die "" bei dem Begriff Engine
-
-
wie ich ja schon erwähnte arbeite ich eigentlich mit CLI (und das auch erst seit 6 Monaten). Da gibt es aber nix gescheites (oder hab nix gefunden) um Komplexere Grafiken zu verwenden. Deshalb wollte ich dafür was eigenes entwickeln um Daten zu Visualisieren (z.B. Darstellung einer Beinprothese und die möglichkeit den Bewegunsablauf zur laufzeit zu optimieren). Da ich aber in anderen Projekten dann nicht wieder bei null anfangen will wollte ich mir die Arbeit machen eine "Engine" (wenn auch eine abgespeckte) für weitere Projekte zu erstellen. Falls mein Ansatz oder der Begriff "Engine" falsch sein sollten könnt ihr mir da natürlich gerne tipps geben wie man das richtig macht. Aber bevor wir jetzt mit der Definition der Engine bzw nicht Engine weiter machen würde ich lieber etwas zum Code erfahren.
-
Nenn's einfach Framework. Engine impliziert irgendwie, dass sie sehr abstrakt für alle möglichen Dinge eingesetzt werden kann. Soll heißen du baust 10 verschiedene Kameratypen ein, obwohl du für deine Anwendung eigentlich nur einen brauchst. Nach
Also sozusagen der Startpunkt um eine Engine aufzubauen.
war ich aber auch schon kurz davor den Link zu posten.
Zum Code:
Bitte mal durchgängig richtig einrücken und farbig machen (cpp-Tags, nicht code-Tags), dann ist das auch etwas einfacher zu lesen.
Ansonsten kann ich mich meinem Vorredner nur anschließen, eine Klasse sollte eine Aufgabe haben, nicht 100. statische Funktionen kann man meistens vermeiden. (Freie Funktionen und OOP sind keine Gegensätze.)
Du kannst ja mal versuchen, mehr in die Konstruktoren/Destruktoren zu verlagern. Funktionen wie "InitGL", "killGLWIndow", "createWindow", etc. sind ein Zeichen für schlechtes Design.
-
Okay, mit Framework kann ich mich anfreunden
Danke für den Hinweis. Werde mich gleich mal ranmachen. Ist ja echt super, so schnell hatte ich nicht mit so vielen Statements gerechnet.