wxWidgets und OpenGL



  • Ich möchte gerne OpenGL lernen und mit der Komponente wxGLCanvas ein 3D-Objekt in einem Panel zeichnen. Ich währe sehr glücklich, wenn mir einer ein einfaches Beispiel posten könnte, wie ich ein einfachen Quader in der Komponente wxPanel von wxWidgets zeichnen kann. Es würde mir als Beispiel ausreichen. Es geht mir nur darum das Prinzip zu verstehen.
    Ich habe in wxWidgets nur mit der Komponente wxPaintDC gearbeitet, um einfache 2D-Spiele zu programmieren. Jetzt möchte ich aber gerne 3D-Spiele mit OpenGL programmieren. Ich kenne mich mit den Programm Povray aus, und kann mir daher 3D-Objekte räumlich sehr gut vorstellen. Aber was nützt es, wenn man die Methoden und Funktionen von OpenGL nicht kennt? Povray ist für 3D-Modelling sehr gut, aber mit wxGLCanvas und wxTimer sowie Tastendruck-Ereignisse kann man ein bisschen Leben rein bringen.



  • http://www.plot3d.net/widgets.html

    Wenn du OpenGL allerdings nicht kennst, findest du unter nehe.gamedev.net ein brauchbares OpenGL Tutorial 🙂



  • Ich habe mir die Beispiele angeschaut und versucht den Quelltext ein wenig umzuschreiben. Hier unten findet Ihr den Quelltext.

    Mein Problem: Ich möchte gerne, dass OpenGL die Graphik ins wxPanel zeichnet. Leider habe ich keine Ahnung wie ich es am geschicktesten anstellen soll. Könnt Ihr mir helfen?

    In mein Quelltext habe ich viele Bugs gefunden. Einige davon sind:

    base.cpp 55: Connect(srFrame::srID_OPENGL_PAINT, wxEVT_PAINT, wxPaintEventHandler(srOpenGL::OnPaint), NULL, this);
    base.cpp 108: wxPaintDC *paint = new wxPaintDC(srFrame::panel);
    

    Ich gebe mir wirklich Mühe den Quelltext, so sauber wie möglich zu schreiben. Es liegt wahrscheinlich daran, dass ich nicht weiß, was OpenGL und wxWidgets von mir erwartet.

    Ich gebe zu, OpenGL ist für mich eine große Sache, aber wie heißt es so schön: Probieren geht über studieren.

    Ich gebe nicht auf und versuche solange an den Quelltext rumzubasteln bis es klappt. Aber damit es nicht so lange dauert, brauche ich euer Rat. Da einige von euch bestimmt mehr Erfahrungen haben als ich.

    base.h

    #ifndef BASE_H
    #define BASE_H
    
    #include <wx/glcanvas.h>
    
    class srApp : public wxApp
    {
    	public:
    		virtual bool OnInit();
    };
    
    class srOpenGL : public wxGLCanvas
    {
    	private:
    		int getWidth();
    		int getHeight();
    		void prepare3DViewport();
    		void OnEraseBackground(wxEraseEvent& event);
    	public:
    		float aspect;
    		wxGLContext* srContext;
    		srOpenGL(wxFrame* frame, const wxSize& size, int* args);
    		~srOpenGL();
    		void Render();
    		void OnActivate(wxActivateEvent& event);
    		void OnPaint(wxPaintEvent& event);
    };
    
    class srFrame : public wxFrame
    {
    	private:
    		int getWidth();
    		int getHeight();
    		wxButton *button;
    	public:
    		int *srWidth;
    		int *srHeight;
    		enum
    		{
    			srID_OPENGL_ACTIVATE,
    			srID_OPENGL_CANVAS,
    			srID_OPENGL_ERASE,
    			srID_OPENGL_PAINT,
    			srID_BUTTON
    		}
    		wxWindowID;
    		wxPanel *panel;
    		srOpenGL *srPanel;
    		srFrame(const wxString& title, const wxPoint& point, const wxSize& size, long style);
    		~srFrame();
    		void OnClick(wxCommandEvent &event);
    		void OnSize(wxSizeEvent &event);
    };
    #endif
    

    base.cpp

    //--------------------------------------------------
    //Autor:	Stefan Rauch	Datum: 5. August 2008
    //--------------------------------------------------
    
    #include <wx/wx.h>
    #include <wx/sizer.h>
    #include <GL/gl.h>
    #include "base.h"
    
    //--------------------------------------------------
    //Der Grundbaustein srApp
    //--------------------------------------------------
    
    IMPLEMENT_APP(srApp)
    
    //Ruft das Awendungsprogramm auf
    bool srApp::OnInit()
    {
    	int width = 800;
    	int height = 600;
    
    	wxBoxSizer *sizer = new wxBoxSizer(wxHORIZONTAL);
    
    	srFrame *frame = new srFrame(wxString("3D-Plot"), wxPoint(0, 0), wxSize(width, height), wxDEFAULT_FRAME_STYLE);
    	frame->srWidth = &width;
    	frame->srHeight = &height;
    
    	int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 32, 0};
    	frame->srPanel = new srOpenGL(frame, wxSize(800, 600), args);
    	frame->srPanel->SetCurrent(*frame->srPanel->srContext);
    
    	sizer->Add(frame->srPanel, 1, wxEXPAND);
    	frame->SetSizer(sizer);
    	frame->SetAutoLayout(TRUE);
    
    	frame->Show(TRUE);
    	frame->Connect(srFrame::srID_BUTTON, wxEVT_COMMAND_BUTTON_CLICKED, wxCommandEventHandler(srFrame::OnClick));
    	frame->Connect(wxEVT_SIZE, wxSizeEventHandler(srFrame::OnSize));
    	SetTopWindow(frame);
    
    	return TRUE;
    }
    
    //--------------------------------------------------
    //Der Baustein srOpenGL
    //--------------------------------------------------
    
    //Der Konstruktor
    srOpenGL::srOpenGL(wxFrame* frame, const wxSize& size, int* args)
    : wxGLCanvas(frame, srFrame::srID_OPENGL_CANVAS, args, wxDefaultPosition, size, 0, wxString("OpenGL"), wxNullPalette)
    {
    	srContext = new wxGLContext(this, NULL);
    	Connect(srFrame::srID_OPENGL_ACTIVATE, wxEVT_ACTIVATE, wxActivateEventHandler(srOpenGL::OnActivate), NULL, this);
    	Connect(srFrame::srID_OPENGL_ERASE, wxEVT_ERASE_BACKGROUND, wxEraseEventHandler(srOpenGL::OnEraseBackground), NULL, this);
    	Connect(srFrame::srID_OPENGL_PAINT, wxEVT_PAINT, wxPaintEventHandler(srOpenGL::OnPaint), NULL, this);
    }
    
    //Der Destruktor
    srOpenGL::~srOpenGL()
    {
    }
    
    //Der Breiten-Akzessor
    int srOpenGL::getWidth()
    {
    	return GetSize().x;
    }
    
    //Der Hoehen-Akzessor
    int srOpenGL::getHeight()
    {
    	return GetSize().y;
    }
    
    //Die Eirichtungsmethoder der 3D-Ansicht
    void srOpenGL::prepare3DViewport()
    {
    	float buttom, fov, left, right, top, zfar, znear;
    	fov = 42.0;
    	znear = 0.3;
    	zfar = 200.0;
    	top = tan(fov * M_PI / 360) * znear;
    	buttom = -top;
    	left = aspect * buttom;
    	right = aspect * top;
    	glClearColor(0.0, 0.0, 0.0, 1.0);
    	aspect = getWidth() / getHeight();
    	glFrustum(left, right, buttom, top, znear, zfar);
    	glMatrixMode(GL_MODELVIEW);
    	glLoadIdentity();
    }
    
    //Die Betriebsmethode
    void srOpenGL::OnActivate(wxActivateEvent& event)
    {
    	prepare3DViewport();
    }
    
    //Die Leerungsmethode der Hintergrundfarbe
    void srOpenGL::OnEraseBackground(wxEraseEvent& WXUNUSED(even))
    {
    	//Befehlsblock zum flimmern der Hintergrundfarbe
    }
    
    //Das Zeichnen-Ereignis
    void srOpenGL::OnPaint(wxPaintEvent &event)
    {
    	wxPaintDC *paint = new wxPaintDC(srFrame::panel);
    	Render();
    	SwapBuffers();
    	delete paint;
    }
    
    //Die Beschreibung der 3D-Objekte
    void srOpenGL::Render()
    {
    	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    	glLoadIdentity();
    
    	//Definiert die sechs Ecken des Quaders
    	double xmin, xmax, ymin, ymax, zmin, zmax;
    	xmin = -0.5;	xmax = 0.5;
    	ymin = -0.5;	ymax = 0.5;
    	zmin = -0.5;	zmax = 0.5;
    
    	glRotatef(25.0, 1.0, 0.0, 0.0);	//hoch und runter
    	glRotatef(35.0, 0.0, 1.0, 0.0); //rechts und links
    	glColor3d(1, 1, 0); //rot, gruen und blau
    
    	//Zeichnet ein Quader aus verbundene Linien
    	glBegin(GL_LINES);
    
    	glVertex3d(ymin, zmin, xmin);
    	glVertex3d(ymin, zmin, xmax);
    
    	glVertex3d(ymin, zmin, xmax);
    	glVertex3d(ymax, zmin, xmax);
    
    	glVertex3d(ymin, zmax, xmax);
    	glVertex3d(ymax, zmax, xmax);
    
    	glVertex3d(ymin, zmin, xmin);
    	glVertex3d(ymax, zmin, xmin);
    
    	glVertex3d(ymin, zmax, xmin);
    	glVertex3d(ymax, zmax, xmin);
    
    	glVertex3d(ymin, zmin, xmax);
    	glVertex3d(ymin, zmax, xmax);
    
    	glVertex3d(ymin, zmax, xmax);
    	glVertex3d(ymin, zmax, xmin);
    
    	glVertex3d(ymin, zmin, xmin);
    	glVertex3d(ymin, zmax, xmin);
    
    	glVertex3d(ymax, zmin, xmin);
    	glVertex3d(ymax, zmin, xmax);
    
    	glVertex3d(ymax, zmin, xmax);
    	glVertex3d(ymax, zmax, xmax);
    
    	glVertex3d(ymax, zmax, xmax);
    	glVertex3d(ymax, zmax, xmin);
    
    	glVertex3d(ymax, zmin, xmin);
    	glVertex3d(ymax, zmax, xmin);
    
    	glEnd();
    }
    
    //--------------------------------------------------
    //Der Baustein srFrame
    //--------------------------------------------------
    
    //Der Konstruktor
    srFrame::srFrame(const wxString& title, const wxPoint& point, const wxSize& size, long style)
    : wxFrame((wxFrame *)NULL, -1, title, point, size, style)
    {
    	panel = new wxPanel(this, srID_OPENGL_PAINT, wxPoint(0, 0), wxSize(800, 600));
    	button = new wxButton(panel, srID_BUTTON, wxString("OK"), wxPoint(5, 5), wxSize(100, 24));
    	panel->SetBackgroundColour(wxColour(0, 0, 0));
    }
    
    //Der Destruktor
    srFrame::~srFrame()
    {
    }
    
    //Der Breiten-Akzessor
    int srFrame::getWidth()
    {
    	return GetSize().x;
    }
    
    //Der Hoehen-Akzessor
    int srFrame::getHeight()
    {
    	return GetSize().y;
    }
    
    //Das Klick-Ereignis
    void srFrame::OnClick(wxCommandEvent &event)
    {
    	Close(TRUE);
    }
    
    //Das Grösse-Ereignis
    void srFrame::OnSize(wxSizeEvent &event)
    {
    	GetClientSize(srWidth, srHeight);
    	glViewport(0, 0, *srWidth, *srHeight);
    	srPanel->SetSize(*srWidth, *srHeight);
    	srPanel->aspect = *srWidth / *srHeight;
    	Refresh();
    }
    


  • Ich habe vergessen den Quelltext mit -lwx_gtk2_gl-2.8 zu linken.

    erstellen.sh

    #!/bin/sh
    
    echo "Der C++-Kompilier wird gestartet..."
    g++ -o programm `wx-config --cppflags` `wx-config --libs` base.cpp -lwx_gtk2_gl-2.8 -Wall
    echo "Die Kompilation wurde beendet."
    ./programm
    

    Ich habe probeweise versucht den wxPanel auszukommentieren und die Graphik auf den Frame zu zeichnen. Leider erhalte ich zur Laufzeit ein Speicherzugriffsfehler.



  • Kann mir keiner helfen?

    Ich habe diesmal DECLARE_EVENT_TABLE() benutzt. g++ hat nicht gemeckert, aber ich erhalte zur Laufzeit ein Speicherzugriffsfehler. Ich bin wirklich ratlos, da ich mit der Komponente wxGLCanvas nicht vertraut bin. Es währe schön, wenn mir einer ein einfaches Quelltext schreiben kann - ohne die Ereignisse wxEVT_SIZE oder wxEVT_BUTTON zu verwenden. Also nur das nötigtste, um mit OpenGL Graphiken in ein wxPanel auszugeben. Den rest schaue ich mir in OpenGL Tutorials von http://nehe.gamedev.net/ an.



  • HAst du mal mit dem Debugger die Stelle gesucht, an der der Fehler auftritt?
    Spontan fällt mir einiges auf:
    -du legst nie eine Instanz von srOpenGL an
    -wenn du den wxGLContext explizit erzeugst, musst du ihn auch mit wxGLCanvas::MakeCurrent() and den Canvas binden, Vorsicht: MakeCurrent auf ein nicht sichtbares Fenster führt unter Windows zum Crash
    -du solltest dir den Umgang mit Sizern in wx anschauen, der richtige Aufbau für dein Beispiel wär wohl wxFrame->wxBoxSizer->wxGLCanvas
    -"normale" Zeichnungsoperationen auf glCanvases sind böse, das Anlegen eines PaintDCs in der onPaint-Methode hat dort also nix zu suchen...

    wxWidgets kann einen echt zur Verzweiflung treiben, da falsche Verwendung oft zu Crashs führt. Ich würde dir wxFormBuilder ans Herz lege, dort kannst du dir deine Oberfläche komfortabel zusammenstellen, und den Umgang mit wxWidgets aus dem generierten Code lernen...

    Grüße

    Martin



  • Ich habe die Beispiele von http://wiki.wxwidgets.org/WxGLCanvas und http://www.plot3d.net/widgets.html. Dort wurde auch wxPaintDC verwendet. Ich wusste nicht, das es nicht empfehlenswert ist diese Komponente anzuwenden. Wie geschrieben, kenne ich nicht alle Mitglieder der Methode wxGLCanvas.

    Ich wusste nicht, dass es für wxWidgets auch eine eigene IDE gibt. Ich werde es mir anschauen.

    Gruß,
    Stefan


Anmelden zum Antworten