Programação com OpenGL/Classe GLXWindow
Introdução
editarA programação orientadas a objetos provem várias facilidades da hora de programar, como o reaproveitamento do código, e até de facilitar o desenvolvimento em grupo por será apresentado aqui uma classe simples de OpenGL para X11 do Linux.
Estrutura da Classe
editarA estrutura da classe SimpleGLXWindow é muito parecido com uma Win32 Api. Este classe herda a classe BOGLGPWindow e então implementa os métodos virtuais. Mostraremos as definições da classe:
class SimpleGLXWindow : public BOGLGPWindow
{
public:
SimpleGLXWindow(); //construtor padrão
virtual ~SimpleGLXWindow();
bool create(int width, int height, int bpp, bool fullscreen);
void destroy();
void processEvents();
void attachExample(Example* example);
bool isRunning(); //Se a janela esta sendo executada?
void swapBuffers() { glXSwapBuffers(m_display, m_XWindow); }
float getElapsedSeconds();
KeyboardInterface* getKeyboard() const { return m_keyboard; }
MouseInterface* getMouse() const { return m_mouse; }
private:
Example* m_example; //uma ligação para o programa de exemplo
bool m_isRunning; //se a janela estiver sendo executada?
Example* getAttachedExample() { return m_example; }
unsigned int m_lastTime;
Display* m_display;
Window m_XWindow;
GLXContext m_glContext;
XF86VidModeModeInfo m_XF86DeskMode;
XSetWindowAttributes m_XSetAttr;
int m_screenID;
bool m_isFullscreen;
unsigned int m_width;
unsigned int m_height;
unsigned int m_bpp;
bool m_GL3Supported;
KeyboardInterface* m_keyboard;
MouseInterface* m_mouse;
};
Criação da Janela
editarA parte mais importante e complicado de uma classe de janela é código que cria a janela OpenGL, A primeira coisa que pegaremos uma manipulador(handle) de tela usando XopenDisplay
, que passaremos o valor zero(ou NULL) isto vai pegar qualquer display, que é configurado no ambiente de variável de DISPLAY( que normalmente é que você esta usando para ver sua Area de trabalho!)
m_display = XOpenDisplay(0); //Open default display
if (m_display == NULL)
{
std::cerr << "Could not open the display" << std::endl;
return false;
}
Assim, tornamos ela o padrão da tela, e se haver algum erro não registrado é retornado falso para indicar um erro na criação da janela. Depois, nós pegaremos um manipulador que identificara o padrão de exibição na tela.
m_screenID = DefaultScreen(m_display); //Get the default screen id
Agora vamos pegar uma lista de modos de tela disponíveis, e veremos se algum é o que queremos. se não teremos que recorrer a um erro:
XF86VidModeModeInfo **modes;
if (!XF86VidModeGetAllModeLines(m_display, m_screenID, &modeNum, &modes))
{
std::cerr << "Could not query the video modes" << std::endl;
return false;
}
int bestMode = -1;
for (int i = 0; i < modeNum; i++)
{
if ((modes[i]->hdisplay == width) &&
(modes[i]->vdisplay == height))
{
bestMode = i;
}
}
if (bestMode == -1)
{
std::cerr << "Could not find a suitable graphics mode" << std::endl;
return false;
}
Depois, nós guardaremos o modo de tela que melhor combina, nós pediremos um double buffer de janela com 16 bit depthbuffer:
int doubleBufferedAttribList [] = {
GLX_RGBA, GLX_DOUBLEBUFFER,
GLX_RED_SIZE, 4,
GLX_GREEN_SIZE, 4,
GLX_BLUE_SIZE, 4,
GLX_DEPTH_SIZE, 16,
None
};
XVisualInfo* vi = NULL;
//Attempt to create a double buffered window
vi = glXChooseVisual(m_display, m_screenID, doubleBufferedAttribList);
if (vi == NULL)
{
std::cerr << "Could not create a double buffered window" << std::endl;
return false;
}
O próximo passo é a criação de um contexto OpenGL 2.1, assim possamos em vez de solicitar uma GL3. Estes são alguns passos que temos que dar em janelas:
//Create a GL 2.1 context
GLXContext gl2Context = glXCreateContext(m_display, vi, 0, GL_TRUE);
if (gl2Context == NULL)
{
std::cerr << "Could not create a GL 2.1 context, please check your graphics drivers" << std::endl;
return false;
}
//Get a pointer to the GL 3.0 context creation
PFNGLXCREATECONTEXTATTRIBSARBPROC glXCreateContextAttribs = (PFNGLXCREATECONTEXTATTRIBSARBPROC)glXGetProcAddress((GLubyte*)"glXCreateContextAttribsARB");
if (glXCreateContextAttribs == NULL)
{
std::cerr << "OpenGL 3.0 is not supported, falling back to 2.1" << std::endl;
m_glContext = gl2Context;
m_GL3Supported = false;
}
else
{
//Create a GL 3.0 context
int attribs[] = {
GLX_CONTEXT_MAJOR_VERSION_ARB, 3,//we want a 3.0 context
GLX_CONTEXT_MINOR_VERSION_ARB, 0,
0 //zero indicates the end of the array
};
m_glContext = glXCreateContextAttribs(m_display, framebufferConfig, 0, true, &attribs[0]);
glXDestroyContext(m_display, gl2Context); //We can destroy the GL 2.0 context once the 3.0 one has bene created
m_GL3Supported = true;
}
Se o OpenGL 3.0 não for suportados, nós vamos setar uma flag assim podemos usar um substituto com shader 2.1
Agora nós temos informaçãoes suficentes para criar uma janela, lembre que nós guardaremos o melhor modo de tela que podemos encontrar? Usamos essa abaixo depois de configurar algumas definições de configuração da janela:
Colormap cmap = XCreateColormap(m_display, RootWindow(m_display, vi->screen),vi->visual, AllocNone);
m_XSetAttr.colormap = cmap;
m_XSetAttr.border_pixel = 0;
m_XSetAttr.event_mask = ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask |
StructureNotifyMask;
m_XSetAttr.override_redirect = False;
unsigned long windowAttributes = CWBorderPixel | CWColormap | CWEventMask;
if (fullscreen)
{
windowAttributes = CWBorderPixel | CWColormap | CWEventMask | CWOverrideRedirect;
XF86VidModeSwitchToMode(m_display, m_screenID, modes[bestMode]);
XF86VidModeSetViewPort(m_display, m_screenID, 0, 0);
m_XSetAttr.override_redirect = True;
}
m_XWindow = XCreateWindow(m_display, RootWindow(m_display, vi->screen),
0, 0, width, height, 0, vi->depth, InputOutput, vi->visual,
CWBorderPixel | CWColormap | CWEventMask, &m_XSetAttr);
Finalmente, vamos colocar um titulo na janela, e se estiver em tela cheia colocaremos um cursor:
if (fullscreen)
{
XWarpPointer(m_display, None, m_XWindow, 0, 0, 0, 0, 0, 0);
XMapRaised(m_display, m_XWindow);
XGrabKeyboard(m_display, m_XWindow, True, GrabModeAsync, GrabModeAsync, CurrentTime);
XGrabPointer(m_display, m_XWindow, True, ButtonPressMask,
GrabModeAsync, GrabModeAsync, m_XWindow, None, CurrentTime);
m_isFullscreen = true;
}
else
{
Atom wmDelete = XInternAtom(m_display, "WM_DELETE_WINDOW", True);
XSetWMProtocols(m_display, m_XWindow, &wmDelete, 1);
XSetStandardProperties(m_display, m_XWindow, title.c_str(), None, NULL, NULL, 0, NULL);
XMapRaised(m_display, m_XWindow);
}
XFree(modes);
Na ultima linha acima libera a memoria para os modos de tela que nós encontramos por ultimo.
Destruindo a Janela
editarÉ muito simples aqui está o código:
void SimpleGLXWindow::destroy()
{
m_mouse->showCursor(true);
if (m_glContext)
{
glXMakeCurrent(m_display, None, NULL);
glXDestroyContext(m_display, m_glContext);
m_glContext = NULL;
}
if (m_isFullscreen)
{
XF86VidModeSwitchToMode(m_display, m_screenID, &m_XF86DeskMode);
XF86VidModeSetViewPort(m_display, m_screenID, 0, 0);
}
XCloseDisplay(m_display);
}
Basicamente nós garantimos que o cursos está visível,depois nós destruímos o contexto OpenGL e finalmente voltamos a modo de tela que esta antes. Nós liberamos nosso manipulador na tela e destruímos complemente a janela.