wxGLCanvas

From WxWiki
Jump to navigation Jump to search
Official Classes SmallBlocks.png Archive Containers Controls Data Structures Database Date & Time Debug Device Contexts Dialogs Document & Views Drag & Drop Events Filesystem Frames Graphics Grid Cell Help HTML Logging Miscellaneous Networking Printing Sizers Streams Threading Windows

wxGLCanvas is a class for displaying OpenGL graphics.

It is always used in conjunction with wxGLContext as the context can only be made current (i.e. active for the OpenGL commands) when it is associated to a wxGLCanvas.

More precisely, you first need to create a wxGLCanvas window and then create an instance of a wxGLContext that is initialized with this wxGLCanvas and then later use either SetCurrent() with the instance of the wxGLContext or wxGLContext::SetCurrent() with the instance of the wxGLCanvas (which might be not the same as was used for the creation of the context) to bind the OpenGL state that is represented by the rendering context to the canvas, and then finally call SwapBuffers() to swap the buffers of the OpenGL canvas and thus show your current output.

Notice that versions of wxWidgets previous to 2.9 used to implicitly create a wxGLContext inside wxGLCanvas itself. This is still supported in the current version but is deprecated now and will be removed in the future, please update your code to create the rendering contexts explicitly.

To set up the attributes for the canvas (number of bits for the depth buffer, number of bits for the stencil buffer and so on) you should set up the correct values of the attribList parameter. The values that should be set up and their meanings will be described below.

Note: On those platforms which use a configure script (e.g. Linux and Mac OS) OpenGL support is automatically enabled if the relative headers and libraries are found. To switch it on under the other platforms (e.g. Windows), you need to edit the setup.h file and set wxUSE_GLCANVAS to 1 and then also pass USE_OPENGL=1 to the make utility. You may also need to add opengl32.lib and glu32.lib to the list of the libraries your program is linked with.

Important note about EVT_SIZE

In linux, adding a handler for EVT_SIZE will mess up OpenGL somehow, and glGenLists(1) stops working. I don't know why ... this might be a bug, but this is the easy workaround here! Hours of head-bashing ended!

SubClassing wxGLCanvas

This is a the smallest sample possible, it will subclass wxGLCanvas and do virtually "no setup", the default values will be used. For more elaborate code that does a proper OpenGL setup, which you will likely want to do in an application, see the other sample below.

// NOTE: To run, it is recommended not to be in Compiz or Beryl, they have shown some instability.

#include <wx/wx.h>
#include <wx/glcanvas.h>

#ifdef __WXMAC__
#include <GLUT/glut.h>
#else
#include <GL/glut.h>
#endif

#ifndef WIN32
#include <unistd.h> // FIXME: ¿This work/necessary in Windows?
                    //Not necessary, but if it was, it needs to be replaced by process.h AND io.h
#endif

class wxGLCanvasSubClass: public wxGLCanvas {
        void Render();
public:
    wxGLCanvasSubClass(wxFrame* parent);
    void Paintit(wxPaintEvent& event);
protected:
    DECLARE_EVENT_TABLE()
};

BEGIN_EVENT_TABLE(wxGLCanvasSubClass, wxGLCanvas)
    EVT_PAINT    (wxGLCanvasSubClass::Paintit)
END_EVENT_TABLE()

wxGLCanvasSubClass::wxGLCanvasSubClass(wxFrame *parent)
:wxGLCanvas(parent, wxID_ANY,  wxDefaultPosition, wxDefaultSize, 0, wxT("GLCanvas")){
    int argc = 1;
    char* argv[1] = { wxString((wxTheApp->argv)[0]).char_str() };

/*
NOTE: this example uses GLUT in order to have a free teapot model
to display, to show 3D capabilities. GLUT, however, seems to cause problems
on some systems. If you meet problems, first try commenting out glutInit(),
then try comeenting out all glut code
*/
    glutInit(&argc, argv);
}


void wxGLCanvasSubClass::Paintit(wxPaintEvent& WXUNUSED(event)){
    Render();
}

void wxGLCanvasSubClass::Render()
{
    SetCurrent();
    wxPaintDC(this);
    glClearColor(0.0, 0.0, 0.0, 0.0);
    glClear(GL_COLOR_BUFFER_BIT);
    glViewport(0, 0, (GLint)GetSize().x, (GLint)GetSize().y);

    glBegin(GL_POLYGON);
        glColor3f(1.0, 1.0, 1.0);
        glVertex2f(-0.5, -0.5);
        glVertex2f(-0.5, 0.5);
        glVertex2f(0.5, 0.5);
        glVertex2f(0.5, -0.5);
        glColor3f(0.4, 0.5, 0.4);
        glVertex2f(0.0, -0.8);
    glEnd();

    glBegin(GL_POLYGON);
        glColor3f(1.0, 0.0, 0.0);
        glVertex2f(0.1, 0.1);
        glVertex2f(-0.1, 0.1);
        glVertex2f(-0.1, -0.1);
        glVertex2f(0.1, -0.1);
    glEnd();

// using a little of glut
    glColor4f(0,0,1,1);
    glutWireTeapot(0.4);

    glLoadIdentity();
    glColor4f(2,0,1,1);
    glutWireTeapot(0.6);
// done using glut

    glFlush();
    SwapBuffers();
}

class MyApp: public wxApp
{
    virtual bool OnInit();
    wxGLCanvas * MyGLCanvas;
};

 
IMPLEMENT_APP(MyApp)
 
  
bool MyApp::OnInit()
{
    wxFrame *frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello GL World"), wxPoint(50,50), wxSize(200,200));
    new wxGLCanvasSubClass(frame);
 
    frame->Show(TRUE);
    return TRUE;
}

See that is added #include <GL/glut.h> and also to the linker remember add the glut library


What follow is a more elaborated source, with input for mouse and keyboard.

Maximize, iconify, fullscreen operations

The examples in wxWidgets react to EVT_SIZE and at the end they use Refresh(); at the en of the function, the problem with this aproach is that it will not handle correctly this 3 "special" events, you will get at screen something that you don't want to see until a paint event is called.

That is why it is pointed out that you need to call Update(); instead of Refresh(); because it will paint immediately the area where the new size is manipulated by iconify, maximize or full screen operations.

Example

Here is a minimal sample with setup to get you started :

#ifndef _glpane_
#define _glpane_
 
#include "wx/wx.h"
#include "wx/glcanvas.h"
 
class BasicGLPane : public wxGLCanvas
{
    wxGLContext*	m_context;

public:
	BasicGLPane(wxFrame* parent, int* args);
	virtual ~BasicGLPane();
    
	void resized(wxSizeEvent& evt);
    
	int getWidth();
	int getHeight();
    
	void render(wxPaintEvent& evt);
	void prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
	void prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y);
    
	// events
	void mouseMoved(wxMouseEvent& event);
	void mouseDown(wxMouseEvent& event);
	void mouseWheelMoved(wxMouseEvent& event);
	void mouseReleased(wxMouseEvent& event);
	void rightClick(wxMouseEvent& event);
	void mouseLeftWindow(wxMouseEvent& event);
	void keyPressed(wxKeyEvent& event);
	void keyReleased(wxKeyEvent& event);
    
	DECLARE_EVENT_TABLE()
};
#endif
#include "wx/wx.h"
#include "wx/sizer.h"
#include "wx/glcanvas.h"
#include "main.h"
 
// include OpenGL
#ifdef __WXMAC__
#include "OpenGL/glu.h"
#include "OpenGL/gl.h"
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
 

class MyApp: public wxApp
{
    virtual bool OnInit();
    
    wxFrame *frame;
    BasicGLPane * glPane;
public:
    
};
 
IMPLEMENT_APP(MyApp)
 
 
bool MyApp::OnInit()
{
    wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);
    frame = new wxFrame((wxFrame *)NULL, -1,  wxT("Hello GL World"), wxPoint(50,50), wxSize(400,200));
	
    int args[] = {WX_GL_RGBA, WX_GL_DOUBLEBUFFER, WX_GL_DEPTH_SIZE, 16, 0};
    
    glPane = new BasicGLPane( (wxFrame*) frame, args);
    sizer->Add(glPane, 1, wxEXPAND);
	
    frame->SetSizer(sizer);
    frame->SetAutoLayout(true);
	
    frame->Show();
    return true;
} 
 
BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
EVT_MOTION(BasicGLPane::mouseMoved)
EVT_LEFT_DOWN(BasicGLPane::mouseDown)
EVT_LEFT_UP(BasicGLPane::mouseReleased)
EVT_RIGHT_DOWN(BasicGLPane::rightClick)
EVT_LEAVE_WINDOW(BasicGLPane::mouseLeftWindow)
EVT_SIZE(BasicGLPane::resized)
EVT_KEY_DOWN(BasicGLPane::keyPressed)
EVT_KEY_UP(BasicGLPane::keyReleased)
EVT_MOUSEWHEEL(BasicGLPane::mouseWheelMoved)
EVT_PAINT(BasicGLPane::render)
END_EVENT_TABLE()
 
 
// some useful events to use
void BasicGLPane::mouseMoved(wxMouseEvent& event) {}
void BasicGLPane::mouseDown(wxMouseEvent& event) {}
void BasicGLPane::mouseWheelMoved(wxMouseEvent& event) {}
void BasicGLPane::mouseReleased(wxMouseEvent& event) {}
void BasicGLPane::rightClick(wxMouseEvent& event) {}
void BasicGLPane::mouseLeftWindow(wxMouseEvent& event) {}
void BasicGLPane::keyPressed(wxKeyEvent& event) {}
void BasicGLPane::keyReleased(wxKeyEvent& event) {}
 
// Vertices and faces of a simple cube to demonstrate 3D render
// source: http://www.opengl.org/resources/code/samples/glut_examples/examples/cube.c
GLfloat v[8][3];
GLint faces[6][4] = {  /* Vertex indices for the 6 faces of a cube. */
    {0, 1, 2, 3}, {3, 2, 6, 7}, {7, 6, 5, 4},
    {4, 5, 1, 0}, {5, 6, 2, 1}, {7, 4, 0, 3} };
 


BasicGLPane::BasicGLPane(wxFrame* parent, int* args) :
    wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
	m_context = new wxGLContext(this);
    // prepare a simple cube to demonstrate 3D render
    // source: http://www.opengl.org/resources/code/samples/glut_examples/examples/cube.c
    v[0][0] = v[1][0] = v[2][0] = v[3][0] = -1;
    v[4][0] = v[5][0] = v[6][0] = v[7][0] = 1;
    v[0][1] = v[1][1] = v[4][1] = v[5][1] = -1;
    v[2][1] = v[3][1] = v[6][1] = v[7][1] = 1;
    v[0][2] = v[3][2] = v[4][2] = v[7][2] = 1;
    v[1][2] = v[2][2] = v[5][2] = v[6][2] = -1;    

    // To avoid flashing on MSW
    SetBackgroundStyle(wxBG_STYLE_CUSTOM);
}
 
BasicGLPane::~BasicGLPane()
{
	delete m_context;
}

void BasicGLPane::resized(wxSizeEvent& evt)
{
//	wxGLCanvas::OnSize(evt);
	
    Refresh();
}
 
/** Inits the OpenGL viewport for drawing in 3D. */
void BasicGLPane::prepare3DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
{
	
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
    glClearDepth(1.0f);	// Depth Buffer Setup
    glEnable(GL_DEPTH_TEST); // Enables Depth Testing
    glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
    glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
	
    glEnable(GL_COLOR_MATERIAL);
	
    glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
	
    float ratio_w_h = (float)(bottomrigth_x-topleft_x)/(float)(bottomrigth_y-topleft_y);
    gluPerspective(45 /*view angle*/, ratio_w_h, 0.1 /*clip close*/, 200 /*clip far*/);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
	
}
 
/** Inits the OpenGL viewport for drawing in 2D. */
void BasicGLPane::prepare2DViewport(int topleft_x, int topleft_y, int bottomrigth_x, int bottomrigth_y)
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
    glEnable(GL_TEXTURE_2D);   // textures
    glEnable(GL_COLOR_MATERIAL);
    glEnable(GL_BLEND);
    glDisable(GL_DEPTH_TEST);
    glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
	
    glViewport(topleft_x, topleft_y, bottomrigth_x-topleft_x, bottomrigth_y-topleft_y);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    
    gluOrtho2D(topleft_x, bottomrigth_x, bottomrigth_y, topleft_y);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}
 
int BasicGLPane::getWidth()
{
    return GetSize().x;
}
 
int BasicGLPane::getHeight()
{
    return GetSize().y;
}
 
 
void BasicGLPane::render( wxPaintEvent& evt )
{
    if(!IsShown()) return;
    
    wxGLCanvas::SetCurrent(*m_context);
    wxPaintDC(this); // only to be used in paint events. use wxClientDC to paint outside the paint event
	
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	
    // ------------- draw some 2D ----------------
    prepare2DViewport(0,0,getWidth()/2, getHeight());
    glLoadIdentity();
	
    // white background
    glColor4f(1, 1, 1, 1);
    glBegin(GL_QUADS);
    glVertex3f(0,0,0);
    glVertex3f(getWidth(),0,0);
    glVertex3f(getWidth(),getHeight(),0);
    glVertex3f(0,getHeight(),0);
    glEnd();
	
    // red square
    glColor4f(1, 0, 0, 1);
    glBegin(GL_QUADS);
    glVertex3f(getWidth()/8, getHeight()/3, 0);
    glVertex3f(getWidth()*3/8, getHeight()/3, 0);
    glVertex3f(getWidth()*3/8, getHeight()*2/3, 0);
    glVertex3f(getWidth()/8, getHeight()*2/3, 0);
    glEnd();
    
    // ------------- draw some 3D ----------------
    prepare3DViewport(getWidth()/2,0,getWidth(), getHeight());
    glLoadIdentity();
	
    glColor4f(0,0,1,1);
    glTranslatef(0,0,-5);
    glRotatef(50.0f, 0.0f, 1.0f, 0.0f);
    
    glColor4f(1, 0, 0, 1);
    for (int i = 0; i < 6; i++)
    {
        glBegin(GL_LINE_STRIP);
        glVertex3fv(&v[faces[i][0]][0]);
        glVertex3fv(&v[faces[i][1]][0]);
        glVertex3fv(&v[faces[i][2]][0]);
        glVertex3fv(&v[faces[i][3]][0]);
        glVertex3fv(&v[faces[i][0]][0]);
        glEnd();
    }
    
    glFlush();
    SwapBuffers();
}

This compiled and worked with 'g++ main.cpp -o gl `wx-config --libs --cxxflags --gl-libs`' (note the extra flag for Wx-Config) plus your platform's flags to link against OpenGL.

Loading OpenGL textures with wxImage

See article Using wxImage to load textures for OpenGL

Support for Win32's MSVC

  • C&C++ / Codegeneration / Release Multithreaded DLL
  • Link :
opengl32.lib wxmsw28d_gl.lib wxmsw.lib
kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib  odbccp32.lib comctl32.lib rpcrt4.lib wsock32.lib  
/nologo /subsystem:windows /incremental:no /pdb:"Release/wxgltest.pdb" /machine:I386 /out:"Release/wxgltest.exe" 

else you'll get this error :

MSVCRT.lib(crtexe.obj) : error LNK2001: unresolved external symbol [_main]

-- RzR 20040613

Support for Win32's Cygwin

$ g++ wxgltest.cpp `wx-config --libs --cxxflags --gl-libs` /usr/lib/gcc-lib/i686-pc-cygwin/3.3.1/../../../../i686-pc-cygwin/bin/ld: cannot find -lwxmsw_gl242 collect2: ld returned 1 exit status

WX should be compiled w/ : ./configure --with-opengl

... [TODO] ...

-- RzR 20040613


KDevelop 3 / Gideon

You need to have wxWidgets compiled with OpenGL / GLCanvas support. Once this is done, create a wxWidgets Project, then open a console and type `wx-config --libs --gl-libs` . Copy the output and paste it into the Project->Project Options -> Configure Options -> Linker Flags (LDFLAGS) textfield.

For KDevelop, it is preferred to edit configure.in.in or configure.in (only if configure.in.in isn't present). You should have a line like : WX_LIBS="`$WXCONFIG --libs`". Just add --gl-libs so it becomes WX_LIBS="`$WXCONFIG --libs --gl-libs`". This way tags will be include in your configure script and make your code more portable (between *nix system).

The previous change sometimes doesn't work for KDevelop 3 or later. If it doesn't, try changing the WX_LIBS line to WX_LIBS="`$WXCONFIG --libs` `$WXCONFIG --gl-libs`".

If that still does not work, then follow the direction below: 1. Create a wxWidgets Project 2. Open a console and type `wx-config --gl-libs` 3. Copy the output and paste it into the Project -> Project Options -> Configure Options -> Linker Flags (LDFLAGS) textfield. 4. Build Project (this will re-run the configure)

Sharing Canvas Context

In order to share OpenGL resources when creating several views of the same objects (display lists, textures, VBOs, ...), you use only one shared wxGLContext. Just create the first wxGLCanvas or inherited object, and then extract its context with wxGLCanvas::GetContext() Then you have to create the rest of the wxGLCanvas using that context.

In wxGTK, the frame upon the wxGLCanvas must be shown before getting the context, because the latter is not created until this happens, so we would be getting an invalid ( not yet created) context:

/* Inside the frame constructor */

canvas1 = new wxGLCanvas( this, wxID_ANY); /* Places the window, but does not create it yet, at least in wxGTK */

Show(); /* Now the OpenGL window is created, so the context is valid */
commonContext = canvas1->GetContext();

canvas2 = new wxGLCanvas( this, commonContext, wxID_ANY); /* Use the same context for the second window. Now you can share textures, display lists, ...*/

Multiple Canvases

I had some trouble getting two wxGLCanvases working at the same time, so when I finally did I thought I would share it with world, so here it is. It is simply meant as an example of how you can make multiple wxGLCanvases with some animation. -- PGP

#include <iostream>
#include <string>
#include <cassert>
#include <cmath>

#include <wx/wx.h>
#include <wx/glcanvas.h>
#include <wx/notebook.h>

class GL_Window : public wxGLCanvas
{
public:

	GL_Window(float c, wxWindow * parent, wxWindowID id,
	          const wxPoint & pos, const wxSize& size, long style=0,
	          const wxString & name = _("GLCanvas"), int * attribList = 0,
	          const wxPalette & palette = wxNullPalette)
	: wxGLCanvas(parent, id, pos, size, style, name, attribList, palette),
	  c_(c), rotate_(c) { }

	virtual ~GL_Window() { }

	void draw() {
		rotate_ += 0.01;

		SetCurrent();
		glClearColor(0.0, 0.0, 0.0, 0.0);
		glClear(GL_COLOR_BUFFER_BIT);
		glViewport(0, 0, (GLint)200, (GLint)200);
		glColor3f(1.0, c_, c_);
		
		glBegin(GL_POLYGON);
		glVertex3f(-0.5, -0.5, 5 * cos(rotate_));
		glVertex3f(-0.5, 0.5, 5 * cos(rotate_));
		glVertex3f(0.5, 0.5, -5 * cos(rotate_));
		glVertex3f(0.5, -0.5, -5 * cos(rotate_));
		glEnd();

		SwapBuffers();
	}

	void OnIdle(wxIdleEvent & event)	{
		draw();
		event.RequestMore();
	}

private:

	float c_;
	float rotate_;
	
	DECLARE_EVENT_TABLE();
};

class MyApp: public wxApp
{
	virtual bool OnInit();
};

IMPLEMENT_APP(MyApp)

bool MyApp::OnInit()
{
	wxFrame* frame = new wxFrame((wxFrame *) NULL, -1,
		_("Hello GL World"), wxPoint(50, 50), wxSize(450, 340) );

	wxNotebook* book = new wxNotebook(frame, -1,
		wxPoint(-1, -1), wxSize(200, 200));

	GL_Window* MyGLCanvas = new GL_Window(1, book, -1, wxPoint(-1, -1),
		wxSize(200, 200), wxSUNKEN_BORDER, _("some text"));
	book->AddPage(MyGLCanvas, _("One"));

	MyGLCanvas = new GL_Window(0, book, -1, wxPoint(-1,-1),
		wxSize(200,200), wxSUNKEN_BORDER, _("some text"));
	book->AddPage(MyGLCanvas, _("Two"));

	frame->Show(true);	
	return true;
}

BEGIN_EVENT_TABLE(GL_Window, wxGLCanvas)
    EVT_IDLE(GL_Window::OnIdle)
END_EVENT_TABLE()

Using on a Panel

No matter what I tried I could not get the wxGLCanvas to work as child of a wxPanel. My goal was to have the same base class for all my visualization classes (2D variants are derived from a wxPanel). As such I wanted the wxGLCanvas to cover the entire wxPanel surface area.

The inheritance scheme is as follows: wxPanel <- PlotBase <- PlotBaseGL

PlotBaseGL would create a wxGLCanvas on top of the wxPanel. After having tried various methods I finally found one that works:

void PlotBaseGL::SetCanvas( void )
{
	delete canvas;

	int attributelist[ 5 ] = { WX_GL_RGBA         ,
	                           WX_GL_BUFFER_SIZE  ,
	                           _colordepth        ,
	                           0                  ,
	                           0                  };

	if ( GetDoubleBuffering() ) attributelist[ 3 ] = WX_GL_DOUBLEBUFFER; 

	Show( false );
	canvas = new wxGLCanvas( this, -1, GetPosition(), GetSize(),
	                         GetWindowStyleFlag(), _T("GLCanvas"),
	                         attributelist, wxNullPalette );

	canvas->Reparent( pwindow ); 
	canvas->SetCurrent();
	canvas->Show( true );                  

	/* some other setup code here */ 
}

It may not be the conventional method but this works fine for me. For some reason creating the wxGLCanvas as a child of the wxPanel and then reparenting to the parent frame solves the problem.


I also had problems getting a wxGLCanvas to display with a wxPanel as parent. I noticed by accident that if I resized the wxPanel just once, the wxGLCanvas would display and update properly. Therefore, I added a hack to resize the wxPanel to be one pixel wider and then back to the original size, and everything worked. You can't just resize to the current size -- apparently wxWindows optimizes this by doing nothing. This hack works on version 2.8.10 on Vista and OS X. The code is in controls.cpp in the Serpent project on SourceForge, and I have not made a small complete example, but the essence is shown below:

    // Note: the first parameter inherits from wxPanel
    Wxs_glcanvas *glcanvas = 
            new Wxs_glcanvas((wxWindow *) wxs_controls[window_id], 
                             WINDOW_ID_BASE + glcanvas_id,
                             wxPoint(x, y), wxSize(w, h));
    wxs_controls[glcanvas_id] = glcanvas; // used by wxserpent to access canvas later
    glcanvas->Refresh(true);
    // HACK: for some reason, canvas does not draw until the parent has
    // been resized, so force a resize here. wxWindows skip the resize
    // processing if the actual size does not change, so change the size
    // and then change it back to get the desired effect.
    glcanvas->GetParent()->GetSize(&x, &y);
    glcanvas->GetParent()->SetSize(-1, -1, x + 1, y, wxSIZE_USE_EXISTING);
    glcanvas->GetParent()->SetSize(-1, -1, x, y, wxSIZE_USE_EXISTING);

Here's yet another solution which I prefer because it requires only one simple change. If you simply add the wxTRANSPARENT_WINDOW style to the style you use when creating the wxGLCanvas then it will trigger the paint event even if you use the same size as the panel. So you simply add that style when creating the canvas directly or when calling the base constructor when sub classing like so:

wxGLCanvas canvas = new wxGLCanvas( this, -1, GetPosition(), GetSize(),
    GetWindowStyleFlag() | wxTRANSPARENT_WINDOW, _T("GLCanvas"),
    attributelist, wxNullPalette );

class MyGLCanvas : public wxGLCanvas
{
public:
    MyGLCanvas(wxString name, wxWindow *parent, wxWindowID id = wxID_ANY, wxPoint pos = wxDefaultPosition, wxSize size = wxDefaultSize, long style = wxFULL_REPAINT_ON_RESIZE)
    : wxGLCanvas(parent, id, NULL, pos, size, style | wxTRANSPARENT_WINDOW, name, wxNullPalette) {}
};

  • Alternate Method - Win32 only

I did this another way. Create a frame that is borderless and has your main frame as a parent. Then always keep the GL frame in the correct screen position. This makes it all very easy and efficient. (My app is called ORIScan)

... .h
		/// <summary>
		/// We catch this because we want to see some of the raw Win32 messages.
		/// For example WM_MOVING is when the frame is dragging with the mouse, which wxFrame does not 
		/// expose.
		/// </summary>
		WXLRESULT MSWWindowProc(WXUINT message,
			WXWPARAM wParam,
			WXLPARAM lParam) wxOVERRIDE;
...  .cpp

ORIScanMainFrame::ORIScanMainFrame(wxWindow* parent)
	:
	ORIScanTopFrameGenerated(parent)
{
	try {
		topController.InitAll();

		oglFrame = new wxFrame(this, -1, wxT("Hello GL World"), wxPoint(50, 50), wxSize(300, 400),
			wxFRAME_FLOAT_ON_PARENT | wxCLIP_CHILDREN);
		//wxBoxSizer* sizer = new wxBoxSizer(wxHORIZONTAL);

		ogl = new BasicGLPane(oglFrame, opengl_args);
		ogl->Show();
		ogl->SetSize(0, 0, 300, 400);
		//bSizer2->Add(glPane);
		oglFrame->Show();
	}
	catch (std::exception& ex) {
		wxBell();
		wxMessageDialog d(this, ex.what());
		d.ShowModal();
		SetStatusText(ex.what());
	}
}

WXLRESULT ORIScanMainFrame::MSWWindowProc(WXUINT message, WXWPARAM wParam, WXLPARAM lParam) {
	WXLRESULT res = ORIScanTopFrameGenerated::MSWWindowProc(message, wParam, lParam);

	// When the user is dragging the window but has not yet let go we want to update the OpenGL
	// frame on every little move. But after wxWidgets has had a chance to handle the event so
	// this is after the call to MSWWindowProc.
	if (message == WM_MOVING) {
		LayoutOpenGLFrame();
	}

	return res;
}

void ORIScanMainFrame::LayoutOpenGLFrame() {
	if (oglFrame->IsVisible() == false)
		return;
	wxRect pt = m_leftSidePanel->GetScreenRect();
	wxPoint pxy = pt.GetTopRight();
	oglFrame->SetPosition(pt.GetTopRight());
	wxRect rr = m_scanTabPane->GetRect();
	wxSize sz(rr.GetWidth() - pt.GetWidth() - 4, pt.GetHeight() - 4);
	oglFrame->SetSize(sz);
}

void ORIScanMainFrame::OnMainFrameIconize(wxIconizeEvent& event) {
	if (event.IsIconized())
		oglFrame->Show(false);
	else {
		oglFrame->Show(true);
		LayoutOpenGLFrame();
	}
	event.Skip();
}

void ORIScanMainFrame::OnMainFrameResize(wxSizeEvent& event) {
	event.Skip();
	LayoutOpenGLFrame();
}

// When the tab pane changes.
void ORIScanMainFrame::OnNotebookPageChanged(wxNotebookEvent& event) {
	if (event.GetSelection() == 0) {
		oglFrame->Show();
		LayoutOpenGLFrame();
	}
	else {
		oglFrame->Show(false);
	}
}

Animation flicker

Usually there will be no flicker issues. If you have problems though, here are a few things to check

If you get weird black flickering when rendering, check that you are not using a wxBufferedDC or wxAutoBufferedDC. OpenGL manages double-buffering on its own, so if you try adding a layer of wx-provided double-buffering on top of that, weirdness may ensue.


Otherwise, for more regular flickering, catch the paint event of the wxGLCanvas (not of the parent frame; if you wish to use event tables then this means deriving from wxGLCanvas) and leave the callback empty. This is usually not necessary

Tearing / Vertical Synchronization (V-Sync)

If you experience screen tearing, or 'laggy' or 'stuttery' mouse motion input (for example when simultaneously redrawing multiple wxGLCanvas), disabling V-Sync may improve this. OpenGL allows overriding the overriding graphics driver V-Sync setting with the swap interval extension series (WGL_EXT_swap_control for Windows, GLX_SWAP_INTERVAL_EXT for Linux). V-Sync is sometimes also referred to as '(vertical) blank interval' or 'v-blank', or in the case of OpenGL extensions, '(vertical) swap interval'.

The following initialization code snippet disables V-Sync on Linux:

#include <GL/glxew.h>

...

 // make the wxGLContext current for this wxGLCanvas
 this->SetCurrent( glcontext );

 // initialize GLEW
 glewInit();

 // disable v-sync
 Display *display = glXGetCurrentDisplay();      // retrieve X11 display handle
 GLXDrawable drawable = glXGetCurrentDrawable(); // retrieve GLX drawable handle
 glXSwapIntervalEXT(display, drawable, 0);       // set swap interval: 0=disabled, 1=enabled, -1=adaptive (EXT_swap_control_tear extension)

This sample uses the GLEW OpenGL extension library in order to load the GLX_SWAP_INTERVAL_EXT extension, and requires linking to X11 and GLEW (-lX11 and -lGLEW compiler options).

See also the OpenGL Wiki: https://www.khronos.org/opengl/wiki/Swap_Interval

Tooltips from upper widgets disappearing or cut when drawn over wxGLCanvas on Windows

If your wxToolBar tooltips are being painted over right after they appear, is probably because you are calling SwapBuffers from inside the wxGLCanvas OnPaint event handler, this seems to cause a clash between GDI and OpenGL ownership of the buffers.

Creating wxPaintDC on the heap (dc = new wxPaintDC(this); delete dc;) so the call to SwapBuffers is inside OnPaint but after EndPaint does nothing, the solution is to create a separate function to update the OpenGL context and place it wherever you call Refresh/Update.

This is a working example, although is missing proper OpenGL initialization (Viewport,etc):

#include "wx/wx.h"
#include "wx/glcanvas.h"

/* XPM */
static char * tooltip_xpm[] = {
"16 16 2 1",
" 	c None",
".	c #000000",
"                ",
" .....       .  ",
" . . .       .  ",
"   .         .  ",
"   . ... ... .  ",
"   . . . . . .  ",
"   . ... ... .. ",
"                ",
"   .....        ",
"   . . .        ",
"     .          ",
"     . . ...    ",
"     . . . .    ",
"     . . ...    ",
"         .      ",
"                "};

class TooltipFix: public wxApp
{
 public:
  virtual bool OnInit();
 private:
  wxFrame* frame;
};

class wxGL: public wxGLCanvas
{
public:
  wxGL(wxWindow *parent, wxWindowID id = wxID_ANY,
        const wxPoint& pos = wxDefaultPosition,
        const wxSize& size = wxDefaultSize, long style = 0,
        const wxString& name = wxGLCanvasName, int *attribList = 0,
        const wxPalette& palette = wxNullPalette)
        : wxGLCanvas(parent,id,pos,size,style,
        name,attribList,palette)
  {
  }
  void Render();
  DECLARE_EVENT_TABLE();
  void OnSize(wxSizeEvent&);
  void OnPaint(wxPaintEvent&);
};

BEGIN_EVENT_TABLE(wxGL, wxGLCanvas)
  EVT_SIZE(wxGL::OnSize)
  EVT_PAINT(wxGL::OnPaint)
END_EVENT_TABLE()

void wxGL::OnSize(wxSizeEvent& event)
{
  Render();
}

void wxGL::OnPaint(wxPaintEvent& event)
{
  wxPaintDC dc(this);
}

void wxGL::Render()
{
  SetCurrent();
  glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
  //.. Add More gl calls here
  SwapBuffers();
}

IMPLEMENT_APP(TooltipFix)

bool TooltipFix::OnInit()
{
  frame = new wxFrame(NULL,-1,_T("OpenGL Tooltip Fix"), wxPoint(0,0),
    wxSize(640,480),
    (long)wxDEFAULT_FRAME_STYLE|wxMAXIMIZE);
  frame->CreateToolBar();
  wxBitmap tooltip_bmp=wxBitmap(wxImage(tooltip_xpm));
  frame->GetToolBar()->AddTool(1,_T("Tool"),tooltip_bmp,_T("Tooltip"));
  frame->GetToolBar()->Realize();
  wxGLCanvas* wxglcanvas = new wxGL(frame,wxID_ANY);
  frame->Show();
  SetTopWindow(frame);
  return true;
}

This is caused by a bug (#10520) in wxToolTip - Net147.

See Also