介绍
在OpenGL游戏编程 - 第二版发布之后,很明显,最后一章中使用的SimpleGLXWindow类没有记载在书中!所以,对于那些喜欢快速开始的Linux用户来说,下面就是了!
类结构
SimpleGLXWindow的类结构与Win32版本非常相似。像Win32版本一样,它继承了BOGLGPWindow基类,因此实现了相同的虚拟方法。这里是类定义的参考:
class SimpleGLXWindow : public BOGLGPWindow
{
public:
SimpleGLXWindow(); //default constructor
virtual ~SimpleGLXWindow();
bool create(int width, int height, int bpp, bool fullscreen);
void destroy();
void processEvents();
void attachExample(Example* example);
bool isRunning(); //Is the window running?
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; //A link to the example program
bool m_isRunning; //Is the window still running?
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;
};
窗口创建
窗口类最重要最复杂的部分是创建OpenGL窗口的代码。我们要做的第一件事就是使用XOpenDisplay获取默认显示的句柄,通过传递一个零(或NULL),这将获取DISPLAY环境变量中设置的任何显示(通常是用来查看桌面的那个) !)。
m_display = XOpenDisplay(0); //Open default display
if (m_display == NULL)
{
std::cerr << "Could not open the display" << std::endl;
return false;
}
所以,我们已经抓住了默认显示,如果有错误,我们已经记录了它,并返回false以指示窗口创建失败。接下来,我们得到一个标识显示器默认屏幕的句柄:
m_screenID = DefaultScreen(m_display); //Get the default screen id
```
现在,我们列出可用的显示模式,看看是否符合我们的要求。如果不是的话,我们会退出一个错误:
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;
}
“`
在我们存储了最佳匹配的显示模式之后,我们要求一个具有16位深度缓冲区的双缓冲窗口:
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;
}
```
下一步是创建一个OpenGL 2.1上下文,所以我们可以请求一个GL3。这些是我们必须采取的与Windows相同的步骤:
//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;
}
“`
如果不支持OpenGL 3.0,我们设置一个标志,以便我们可以使用后备2.1着色器。
现在我们有足够的信息来实际创建窗口。记得我们存储了我们能找到的最好的显示模式吗?在设置了一些窗口配置设置后,我们使用下面的代码:
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);
```
最后,我们设置窗口标题,如果我们是全屏,我们抓住光标:
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);
上面的最后一行释放了我们之前搜索的显示模式的内存。
破坏窗口
这很简单,代码如下:
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);
}
“`
基本上,我们确保光标是可见的,然后我们销毁OpenGL上下文,最后在完成时切换显示模式。我们在显示器上释放我们的手柄,窗户被完全破坏。
如果您还有其他问题,无论是关于本文还是本书,或者在论坛上告诉我,或者在Twitters上戳我。