Probleme mit 2 OpenGL-Fenstern



  • OpenGL ist eine Statusmaschine.
    Diese existiert einmal pro Anwendung(Multithreading jetzt mal außen vor gelassen).

    Dies bedeutet folgerichtig, dass du nur eine Matrix von der jeweiligen Art hast usw.
    Die Lösung liegt einfach darin, wenn man schaut, wie OpenGL in ein Windowsfenster rendert. DOrt verbindest du ein Renderkontext von OpenGL mit einem DeviceKontext von einem Windowsfenster.
    Wenn du jetzt also in ein anderes Fenster willst, musst du nur den aktuellen Devicekontext neu setzen, nämlich auf das gerade aktuelle Fenster. Das machst du mit wglMakeCurrent und das immer, bevor die ersten fensterspefiischen OpenGL-Befehle kommen.



  • Ich habe nun genau das gleiche Problem. Ich hatte bisher folgende Struktur:

    - Hauptklasse
    - Tab-Nummer1-Klasse: OpenGLCreate und TimerActivate
    - Tab-Nummer2-Klasse: neu, soll dasselbe wie Tab1 machen
    - OpenGLCtrl-Klasse

    So funktioniert es anscheinend nicht, wie ich hier lese. Wie muss ich nun die logische Struktur ändern?

    - Hauptklasse: OpenGLCreate
    - Tab-Nummer1-Klasse: wglMakeCurrent und TimerActivate
    - Tab-Nummer2-Klasse: wglMakeCurrent und TimerActivate
    - OpenGLCtrl-Klasse

    So? Die Hauptklasse bereitet das OpenGL vor und bindet standardmässig an das Static im Tab1? Wenn ich dann im Tab2 bin, wird das wglMakeCurrent und der RenderTimer aktiviert? Beim Umschalten wird dann immer wglMakeCurrent ausgeführt. Bloss wo genau muss dieses wglmakeCurrent hin? In die OpenGLCtrl-Klasse? Wie genau sieht der Befehl aus? Danke für Eure Hilfe!



  • Als erstens musst du für beide Fenster nen eigenen Pixeldesxriptor machen. Am besten irgenwo ne init-Methode die du dann aufrufst für jedes Fenster.(wenn das dein OpenGLCreate darstellen soll, dann muss es in beide Tabklassen)

    Ansonsten schreibt du einfach vor deinem ersten fensterspezifischen OpenGL-Befehl

    wglMakeCurrent(hDC,hRC);
    

    Den Device- und Rendercontext legst du ja in der init-Methode an für jedes Fenster(werden also Membervariable der Klasse sein).

    Das wars eigentlich schon.

    Für das OpenGL-Zeugs empfiehlt es sich, ne eigene KLasse zu schreiben, welche du dann in allen Fenstern benutzt.



  • Hmmmm... kannst Du etwas spezifischer werden? Hier mal etwas Code:

    Aus meiner Tab1-Klasse:

    BOOL tabDialog2::OnInitDialog()
    {
    	// Get size and position of the template textfield we created before in the dialog editor
    	GetDlgItem(IDC_OPENGL)->GetWindowRect(rect_opengl);
    
    	// Convert screen coordinates to client coordinates
    	ScreenToClient(rect_opengl);
    
    	// Create OpenGL Control window
    	m_oglGraph->oglCreate(rect_opengl, this);
    
    	// return 0 if everything went fine
    	return 0;
    }
    

    Aus der OpenGLCtrl Klasse:

    int COpenGLCtrl::OnCreate(LPCREATESTRUCT lpCreateStruct)
    {
            if (CWnd::OnCreate(lpCreateStruct) == -1)
    		return -1;
    
            oglInitialize();
    
            return 0;
    }
    
    void COpenGLCtrl::oglCreate(CRect rect, CWnd *parent)
    {
        CString className = AfxRegisterWndClass(CS_HREDRAW | CS_VREDRAW | CS_OWNDC, NULL,
    	  (HBRUSH)GetStockObject(BLACK_BRUSH), NULL);
    
        CreateEx(0, className, "OpenGL", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, rect, parent, 0);
    
        // Set initial variables' values
        m_oldWindow    = rect;
        m_originalRect = rect;
    
        hWnd = parent;
    }
    
    void COpenGLCtrl::OnPaint()
    {
            CPaintDC dc(this); // device context for painting
            ValidateRect(NULL);
    }
    
    void COpenGLCtrl::oglInitialize(void)
    {
    	// Initial Setup:
        //
        static PIXELFORMATDESCRIPTOR pfd =
        {
    		sizeof(PIXELFORMATDESCRIPTOR),
            1,
            PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
            PFD_TYPE_RGBA,
            32, // bit depth
            0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
            16, // z-buffer depth
            0, 0, 0, 0, 0, 0, 0,
    	};
    
        // Get device context only once.
        hdc = GetDC()->m_hDC;
    
        // Pixel format.
        m_nPixelFormat = ChoosePixelFormat(hdc, &pfd);
        SetPixelFormat(hdc, m_nPixelFormat, &pfd);
    
        // Create the OpenGL Rendering Context.
        hrc = wglCreateContext(hdc);
        wglMakeCurrent(hdc, hrc);
    
        // Basic Setup:
        //
        // Set color to use when clearing the background.
        glClearColor(0.6f, 0.6f, 0.6f, 1.0f);
    	glClearDepth(1.0f);
    
    	// Turn on backface culling
    	glFrontFace(GL_CCW);
    	glCullFace(GL_BACK);
    
    	// Turn on depth testing
    	glEnable(GL_DEPTH_TEST);
    	glDepthFunc(GL_LEQUAL);
    
        // Send draw request
        OnDraw(NULL);
    }
    

    Was fehlt ist noch das Timer-Event zum Neuzeichnen, aber das ist hier sicher nicht so wichtig. Ich blicke bei OpenGL noch nichts ganz so durch, daher frage ich nochmal etwas genauer 🙂 ich bin ja schon froh, dass es mit einem OpenGL Window läuft 😉



  • Du rufst in der OnInitDialog nicht oglInitialize() auf. Das muss aber einmal geschehen. Liegts evtl. nur daran?



  • Ich werds mal testen. Ich schiebe es aber erstmal in meiner ToDo-Liste nach unten, ich teste erstmal NUR das zweite OGL-Fenster. Wenn das läuft versuche ich beide gleichzeitig, wenn was ist melde ich mich einfach nochmal 🙂



  • Mach das 🙂 An sich ist es halt wirklich so, dass du alles doppelt hast. Da wir doppeltes nicht mögen, lohnt sich dafür natürlich ne eigene Klasse^^



  • So, bin nun wieder an diesem Problem. Irgendwann muss es ja gelöst werden 😉 Ich rätsel immernoch, wieso es 2 Pixeldeskriptoren sein müssen, weil in meinem bestehenden keine spezifischen Fensterinformationen drinstehen. Würde ich das machen, sähen beide Deskriptoren identisch aus.

    Mal eine andere Überlegung: Macht es Sinn, das OpenGL Control beim Verlassen des TabX zu zerstören und beim Entern von TabY neu zu createn? Darf man das mehrmals machen? Da käme ja dann dasselbe bei raus oder?



  • JPSelter schrieb:

    So, bin nun wieder an diesem Problem. Irgendwann muss es ja gelöst werden 😉 Ich rätsel immernoch, wieso es 2 Pixeldeskriptoren sein müssen, weil in meinem bestehenden keine spezifischen Fensterinformationen drinstehen. Würde ich das machen, sähen beide Deskriptoren identisch aus.

    OpenGL will nunmal ne Verbindung vom Rendercontext zunm Devicecontext und das für jede Verbindung, sprich also für jedes Fenster. Sozusagen "Liebes liebes OpenGL, rendere bitte in diesen DC mit diesen gesetzten Eigenschaften"
    Und das geht nunmal über den Pixeldeskriptor. Das du dann zweimal den selben setzt, ist doch egal dabei.

    JPSelter schrieb:

    Mal eine andere Überlegung: Macht es Sinn, das OpenGL Control beim Verlassen des TabX zu zerstören und beim Entern von TabY neu zu createn? Darf man das mehrmals machen? Da käme ja dann dasselbe bei raus oder?

    Musst du halt mal testen. ich würd aber sagen ja musst du machen. Ein neuer Tab hat nen neuen Handle und für das neue Handle brauchst du nen neuen DeviceContext. Diesen musst dann wieder mit dem Rendercontext verbinden usw.
    Wie gesagt einfach mal testen^^



  • OK, soweit so gut. Meine Software zeichnet nun beide, aber nur den, der zuerst erzeugt wird und danach NUR den, also das Tab, das zuerst angeklickt wird. Alles was jetzt noch fehlt, ist eine Destroy-Methode. Jetzt sehe ich wieder, wie wenig Ahnung ich von C++ habe 😃 Wie zerstöre ich denn das OpenGL-Objekt wieder?

    if (m_tabctrlMain.GetCurSel()==1)
    	{
    		((tabDialog2*)m_tabctrlMain.GetTab(1))->DestroyOpenGL();
    // plus eine Abfrage ob es überhaupt schon ein Objekt gibt
    		((tabDialog2*)m_tabctrlMain.GetTab(1))->CreateOpenGL();
    		((tabDialog2*)m_tabctrlMain.GetTab(1))->ActivateRenderTimer();
    	}
    
    	if (m_tabctrlMain.GetCurSel()==2)
    	{
    		((tabDialog2*)m_tabctrlMain.GetTab(2))->DestroyOpenGL();
    // plus eine Abfrage ob es überhaupt schon ein Objekt gibt
    		((tabDialog3*)m_tabctrlMain.GetTab(2))->CreateOpenGL();
    		((tabDialog3*)m_tabctrlMain.GetTab(2))->ActivateRenderTimer();
    	}
    

    So ein CreateOpenGL sieht im Moment so aus (Beispiel aus tabDialog2):

    void tabDialog2::CreateOpenGL()
    {
    	m_oglGraph = new COpenGLCtrl(); <---- hier wirds konstruiert
    
    	m_oglGraph->SetOGLDrawType(0);
    
    	m_boolTimerActive=false;
    
    	GetDlgItem(IDC_OPENGL)->GetWindowRect(rect_opengl);
    
    	ScreenToClient(rect_opengl);
    
    	// Create OpenGL Control window
    	m_oglGraph->oglCreate(rect_opengl, this);
    }
    

    Wie sieht das DestroyOpenGL() aus? Sowas wie m_oglGraph.destroy() oder so? Das gibt es leider nur nicht. Vielen Dank für die Hilfe!



  • Habs gleich selbst gefunden 🙂 Ich rufe einfach den Destruktor von COpenGLCtrl auf. Jetzt muss ich nur noch überprüfen, ob das Objekt vorher schon da war.



  • edit: ok hat sich ja erledigt 🙂



  • Leider noch nicht ganz. Irgendwie wird das OpenGL nicht richtig gekillt. Ein Wechsel zwischen Tab2 - Tab3 - Tab2 - Tab3 endet in einer Exception, zudem addieren sich immer 2MB zum RAM hinzu. Die Exception kommt laut dem Debugger aus der Zeile

    CreateEx(0, className, "OpenGL", WS_VISIBLE | WS_CHILD | WS_CLIPSIBLINGS, rect, parent, 0);

    aus meinem OpenGLCreate. Wie kann ich das gesamte OpenGL killen? Gibts doch bestimmt Befehle für oder?



  • Mit Create legst du ein Windowsfenster an. Das hat noch nix mit OpenGL zu tun. Dieses sollte man auch wieder mit Destroy freigeben.

    Dann holst du dir sicher irgendwo mit GetDC() nen Context. Dieses gibt man am Ende mit ReleaseDC wieder frei.
    Und den Rendercontext löscht man mit wglDeleteContext. Das wäre dann das einzig OpenGL-spezifische.



  • Au mann, langsam drehe ich durch... kostet schonwieder viel zu viel zeit alles. In einem Tutorial steht, ich soll

    ReleaseDC(hWnd,hdc)

    benutzen, aber der Compiler meckert rum, er akzeptiert keine 2 Argumente. Dann lasse ich hWnd weg und nun meckert er rum

    error C2664: 'CWnd::ReleaseDC' : cannot convert parameter 1 from 'HDC' to 'CDC *'

    Gleich bei wglDeleteContext dann sicher dasselbe Spiel...



  • Wie holst du dir den DC?
    Mit GetDC() oder mit GetDC(HWND); ?

    Bei zweiter Variante dann gib das frei mit **::**GetDC(HWND,HDC)

    Ersteres ist nämlich ne Membermethode von CWnd, zweiteres ist ne WinAPI-Funktion



  • mit

    hdc = GetDC()->m_hDC;
    


  • OK, hier hab ich was gefunden:

    http://steinsoft.net/index.php?site=Programming/Tutorials/opengl_dialog&printable=1

    Ist so ähnlich wie mein Code jetzt, sogar sehr ähnlich. Das Tutorial was ich benutzt habe ist leider wieder offline. Aber hiermit können wir schnell eine Antwort finden 🙂 Einfache Frage: Was würdet Ihr an diesem Tutorial verändern, damit man das Zielfenster während der Laufzeit ändern kann? Mein Problem ist einfach, dass ich da dieses "*parent" in ::Create sehe und dieses nur einmal aufgerufen wird. Also was muss verändert werden? Danke für weitere Hilfe!



  • Hm an sich nix^^
    Der Code beschreibt ja ein OpenGL-Control, ob du dann nun 1, 2 oder 3 anlegst ist dabei egal.
    Nur das Zeichnen in der OnPaint musst du anders realisieren, das du das zu zeichnende von außen setzen kannst.

    Was man nicht sieht momentan ist die Freigabe, aber die kann auch in der OpenGLDevice-Klassen "versteckt" sein.



  • Ich dachte, man könne nur eines davon haben? Am Anfang dieses Threads wurde das zumindest gesagt. Ich meiner Version dieser Klasse habe ich bereits eine Membervariable, die den Output steuert. Was gezeichnet wird, ist also nicht das Problem, sondern das "wohin". Ganz am Anfang hatte ich ja 2 Instanzen dieser Klasse, aber da bekam eine immer seltsame Streifen und Linien (wie ein TV das keinen Empfang hat)

    *15 Minuten später*

    Habe mal eben mit dem Typen von der oben verlinkten Seite gechattet. Er hat ein kleines Demoprogramm mit Code, wo er gleichzeitig in 9 Fenster rendert. Also das was ich suche. Er benutzt Threads, anscheinend klappts bei mir deshalb nicht gleichzeitig.


Anmelden zum Antworten