summaryrefslogtreecommitdiff
path: root/g_src/renderer_opengl.hpp
diff options
context:
space:
mode:
Diffstat (limited to 'g_src/renderer_opengl.hpp')
-rwxr-xr-xg_src/renderer_opengl.hpp609
1 files changed, 609 insertions, 0 deletions
diff --git a/g_src/renderer_opengl.hpp b/g_src/renderer_opengl.hpp
new file mode 100755
index 0000000..0ea3c6c
--- /dev/null
+++ b/g_src/renderer_opengl.hpp
@@ -0,0 +1,609 @@
+// STANDARD
+class renderer_opengl : public renderer {
+public:
+ virtual bool uses_opengl() { return true; }
+
+protected:
+ SDL_Surface *screen;
+
+ int dispx, dispy; // Cache of the current font size
+
+ bool init_video(int w, int h) {
+ // Get ourselves an opengl-enabled SDL window
+ Uint32 flags = SDL_HWSURFACE | SDL_OPENGL;
+
+ // Set it up for windowed or fullscreen, depending.
+ if (enabler.is_fullscreen()) {
+ flags |= SDL_FULLSCREEN;
+ } else {
+ if (!init.display.flag.has_flag(INIT_DISPLAY_FLAG_NOT_RESIZABLE))
+ flags |= SDL_RESIZABLE;
+ }
+
+ // Setup OpenGL attributes
+ SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, init.window.flag.has_flag(INIT_WINDOW_FLAG_VSYNC_ON));
+ SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER,
+ init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER) ? 0 : 1);
+
+ // (Re)create the window
+ screen = SDL_SetVideoMode(w, h, 32, flags);
+
+ if (!screen) return false;
+
+ // Test double-buffering status
+ int test;
+ SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &test);
+ if (test != ((init.display.flag.has_flag(INIT_DISPLAY_FLAG_SINGLE_BUFFER)) ? 0 : 1)) {
+ if (enabler.is_fullscreen());
+ //errorlog << "Requested single-buffering not available\n" << flush;
+ else
+ report_error("OpenGL", "Requested single-buffering not available");
+ }
+
+ // (Re)initialize GLEW. Technically only needs to be done once on
+ // linux, but on windows forgetting will cause crashes.
+ glewInit();
+
+ // Set the viewport and clear
+ glViewport(0, 0, screen->w, screen->h);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ return true;
+ }
+
+ // Vertexes, foreground color, background color, texture coordinates
+ GLfloat *vertexes, *fg, *bg, *tex;
+
+ void write_tile_vertexes(GLfloat x, GLfloat y, GLfloat *vertex) {
+ vertex[0] = x; // Upper left
+ vertex[1] = y;
+ vertex[2] = x+1; // Upper right
+ vertex[3] = y;
+ vertex[4] = x; // Lower left
+ vertex[5] = y+1;
+ vertex[6] = x; // Lower left again (triangle 2)
+ vertex[7] = y+1;
+ vertex[8] = x+1; // Upper right
+ vertex[9] = y;
+ vertex[10] = x+1; // Lower right
+ vertex[11] = y+1;
+ }
+
+ virtual void allocate(int tiles) {
+ vertexes = static_cast<GLfloat*>(realloc(vertexes, sizeof(GLfloat) * tiles * 2 * 6));
+ assert(vertexes);
+ fg = static_cast<GLfloat*>(realloc(fg, sizeof(GLfloat) * tiles * 4 * 6));
+ assert(fg);
+ bg = static_cast<GLfloat*>(realloc(bg, sizeof(GLfloat) * tiles * 4 * 6));
+ assert(bg);
+ tex = static_cast<GLfloat*>(realloc(tex, sizeof(GLfloat) * tiles * 2 * 6));
+ assert(tex);
+
+ glEnableClientState(GL_VERTEX_ARRAY);
+ glVertexPointer(2, GL_FLOAT, 0, vertexes);
+ }
+
+ virtual void init_opengl() {
+ enabler.textures.upload_textures();
+ }
+
+ virtual void uninit_opengl() {
+ enabler.textures.remove_uploaded_textures();
+ }
+
+ virtual void draw(int vertex_count) {
+ // Render the background colors
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_BLEND);
+ glDisable(GL_ALPHA_TEST);
+ glColorPointer(4, GL_FLOAT, 0, bg);
+ glDrawArrays(GL_TRIANGLES, 0, vertex_count);
+ // Render the foreground, colors and textures both
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_NOTEQUAL, 0);
+ glEnable(GL_TEXTURE_2D);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glColorPointer(4, GL_FLOAT, 0, fg);
+ glDrawArrays(GL_TRIANGLES, 0, vertex_count);
+
+ printGLError();
+ }
+
+ void write_tile_arrays(int x, int y, GLfloat *fg, GLfloat *bg, GLfloat *tex) {
+ Either<texture_fullid,texture_ttfid> id = screen_to_texid(x, y);
+ if (id.isL) { // An ordinary tile
+ const gl_texpos *txt = enabler.textures.gl_texpos;
+ // TODO: Only bother to set the one that's actually read in flat-shading mode
+ // And set flat-shading mode.
+ for (int i = 0; i < 6; i++) {
+ *(fg++) = id.left.r;
+ *(fg++) = id.left.g;
+ *(fg++) = id.left.b;
+ *(fg++) = 1;
+
+ *(bg++) = id.left.br;
+ *(bg++) = id.left.bg;
+ *(bg++) = id.left.bb;
+ *(bg++) = 1;
+ }
+ // Set texture coordinates
+ *(tex++) = txt[id.left.texpos].left; // Upper left
+ *(tex++) = txt[id.left.texpos].bottom;
+ *(tex++) = txt[id.left.texpos].right; // Upper right
+ *(tex++) = txt[id.left.texpos].bottom;
+ *(tex++) = txt[id.left.texpos].left; // Lower left
+ *(tex++) = txt[id.left.texpos].top;
+
+ *(tex++) = txt[id.left.texpos].left; // Lower left
+ *(tex++) = txt[id.left.texpos].top;
+ *(tex++) = txt[id.left.texpos].right; // Upper right
+ *(tex++) = txt[id.left.texpos].bottom;
+ *(tex++) = txt[id.left.texpos].right; // Lower right
+ *(tex++) = txt[id.left.texpos].top;
+ } else {
+ // TODO
+ }
+ }
+
+public:
+ void update_tile(int x, int y) {
+ const int tile = x*gps.dimy + y;
+ // Update the arrays
+ GLfloat *fg = this->fg + tile * 4 * 6;
+ GLfloat *bg = this->bg + tile * 4 * 6;
+ GLfloat *tex = this->tex + tile * 2 * 6;
+ write_tile_arrays(x, y, fg, bg, tex);
+ }
+
+ void update_all() {
+ glClear(GL_COLOR_BUFFER_BIT);
+ for (int x = 0; x < gps.dimx; x++)
+ for (int y = 0; y < gps.dimy; y++)
+ update_tile(x, y);
+ }
+
+ void render() {
+ draw(gps.dimx*gps.dimy*6);
+ if (init.display.flag.has_flag(INIT_DISPLAY_FLAG_ARB_SYNC) && GL_ARB_sync) {
+ assert(enabler.sync == NULL);
+ enabler.sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
+ }
+ SDL_GL_SwapBuffers();
+ }
+
+ renderer_opengl() {
+ // Init member variables so realloc'll work
+ screen = NULL;
+ vertexes = NULL;
+ fg = NULL;
+ bg = NULL;
+ tex = NULL;
+ zoom_steps = forced_steps = 0;
+
+ // Disable key repeat
+ SDL_EnableKeyRepeat(0, 0);
+ // Set window title/icon.
+ SDL_WM_SetCaption(GAME_TITLE_STRING, NULL);
+ SDL_Surface *icon = IMG_Load("data/art/icon.png");
+ if (icon != NULL) {
+ SDL_WM_SetIcon(icon, NULL);
+ // The icon's surface doesn't get used past this point.
+ SDL_FreeSurface(icon);
+ }
+
+ // Find the current desktop resolution if fullscreen resolution is auto
+ if (init.display.desired_fullscreen_width == 0 ||
+ init.display.desired_fullscreen_height == 0) {
+ const struct SDL_VideoInfo *info = SDL_GetVideoInfo();
+ init.display.desired_fullscreen_width = info->current_w;
+ init.display.desired_fullscreen_height = info->current_h;
+ }
+
+ // Initialize our window
+ bool worked = init_video(enabler.is_fullscreen() ?
+ init.display.desired_fullscreen_width :
+ init.display.desired_windowed_width,
+ enabler.is_fullscreen() ?
+ init.display.desired_fullscreen_height :
+ init.display.desired_windowed_height);
+
+ // Fallback to windowed mode if fullscreen fails
+ if (!worked && enabler.is_fullscreen()) {
+ enabler.fullscreen = false;
+ report_error("SDL initialization failure, trying windowed mode", SDL_GetError());
+ worked = init_video(init.display.desired_windowed_width,
+ init.display.desired_windowed_height);
+ }
+ // Quit if windowed fails
+ if (!worked) {
+ report_error("SDL initialization failure", SDL_GetError());
+ exit(EXIT_FAILURE);
+ }
+
+ // Initialize opengl
+ init_opengl();
+ }
+
+ virtual ~renderer_opengl() {
+ free(vertexes);
+ free(fg);
+ free(bg);
+ free(tex);
+ }
+
+ int zoom_steps, forced_steps;
+ int natural_w, natural_h; // How large our view would be if it wasn't zoomed
+
+ void zoom(zoom_commands cmd) {
+ pair<int,int> before = compute_zoom(true);
+ int before_steps = zoom_steps;
+ switch (cmd) {
+ case zoom_in: zoom_steps -= init.input.zoom_speed; break;
+ case zoom_out: zoom_steps += init.input.zoom_speed; break;
+ case zoom_reset:
+ zoom_steps = 0;
+ case zoom_resetgrid:
+ compute_forced_zoom();
+ break;
+ }
+ pair<int,int> after = compute_zoom(true);
+ if (after == before && (cmd == zoom_in || cmd == zoom_out))
+ zoom_steps = before_steps;
+ else
+ reshape(after);
+ }
+
+ void compute_forced_zoom() {
+ forced_steps = 0;
+ pair<int,int> zoomed = compute_zoom();
+ while (zoomed.first < MIN_GRID_X || zoomed.second < MIN_GRID_Y) {
+ forced_steps++;
+ zoomed = compute_zoom();
+ }
+ while (zoomed.first > MAX_GRID_X || zoomed.second > MAX_GRID_Y) {
+ forced_steps--;
+ zoomed = compute_zoom();
+ }
+ }
+
+ pair<int,int> compute_zoom(bool clamp = false) {
+ const int dispx = enabler.is_fullscreen() ?
+ init.font.large_font_dispx :
+ init.font.small_font_dispx;
+ const int dispy = enabler.is_fullscreen() ?
+ init.font.large_font_dispy :
+ init.font.small_font_dispy;
+ int w, h;
+ if (dispx < dispy) {
+ w = natural_w + zoom_steps + forced_steps;
+ h = double(natural_h) * (double(w) / double(natural_w));
+ } else {
+ h = natural_h + zoom_steps + forced_steps;
+ w = double(natural_w) * (double(h) / double(natural_h));
+ }
+ if (clamp) {
+ w = MIN(MAX(w, MIN_GRID_X), MAX_GRID_X);
+ h = MIN(MAX(h, MIN_GRID_Y), MAX_GRID_Y);
+ }
+ return make_pair(w,h);
+ }
+
+ // Parameters: grid units
+ void reshape(pair<int,int> size) {
+ int w = MIN(MAX(size.first, MIN_GRID_X), MAX_GRID_X);
+ int h = MIN(MAX(size.second, MIN_GRID_Y), MAX_GRID_Y);
+#ifdef DEBUG
+ cout << "Resizing grid to " << w << "x" << h << endl;
+#endif
+ gps_allocate(w, h);
+ reshape_gl();
+ }
+
+ int off_x, off_y, size_x, size_y;
+
+ bool get_mouse_coords(int &x, int &y) {
+ int mouse_x, mouse_y;
+ SDL_GetMouseState(&mouse_x, &mouse_y);
+ mouse_x -= off_x; mouse_y -= off_y;
+ if (mouse_x < 0 || mouse_y < 0 ||
+ mouse_x >= size_x || mouse_y >= size_y)
+ return false; // Out of bounds
+ x = double(mouse_x) / double(size_x) * double(gps.dimx);
+ y = double(mouse_y) / double(size_y) * double(gps.dimy);
+ return true;
+ }
+
+ virtual void reshape_gl() {
+ // Allocate array memory
+ allocate(gps.dimx * gps.dimy);
+ // Initialize the vertex array
+ int tile = 0;
+ for (GLfloat x = 0; x < gps.dimx; x++)
+ for (GLfloat y = 0; y < gps.dimy; y++, tile++)
+ write_tile_vertexes(x, y, vertexes + 6*2*tile);
+ // Setup invariant state
+ glEnableClientState(GL_COLOR_ARRAY);
+ /// Set up our coordinate system
+ if (forced_steps + zoom_steps == 0 &&
+ init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) {
+ size_x = gps.dimx * dispx;
+ size_y = gps.dimy * dispy;
+ off_x = (screen->w - size_x) / 2;
+ off_y = (screen->h - size_y) / 2;
+ } else {
+ // If we're zooming (or just not using black space), we use the
+ // entire window.
+ size_x = screen->w;
+ size_y = screen->h;
+ off_x = off_y = 0;
+ }
+ glViewport(off_x, off_y, size_x, size_y);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluOrtho2D(0, gps.dimx, gps.dimy, 0);
+ }
+
+ // Parameters: window size
+ void resize(int w, int h) {
+ // (Re)calculate grid-size
+ dispx = enabler.is_fullscreen() ?
+ init.font.large_font_dispx :
+ init.font.small_font_dispx;
+ dispy = enabler.is_fullscreen() ?
+ init.font.large_font_dispy :
+ init.font.small_font_dispy;
+ natural_w = MAX(w / dispx,1);
+ natural_h = MAX(h / dispy,1);
+ // Compute forced_steps so we satisfy our grid-size limits
+ compute_forced_zoom();
+ // Force a full display cycle
+ gps.force_full_display_count = 1;
+ enabler.flag |= ENABLERFLAG_RENDER;
+ // Reinitialize the video
+ uninit_opengl();
+ init_video(w, h);
+ init_opengl();
+ // Only reshape if we're free to pick grid size
+ if (enabler.overridden_grid_sizes.size() == 0)
+ reshape(compute_zoom());
+ }
+
+ // Parameters: grid size
+ void grid_resize(int w, int h) {
+ reshape(make_pair(w, h));
+ }
+
+public:
+ void set_fullscreen() {
+ if (enabler.is_fullscreen()) {
+ init.display.desired_windowed_width = screen->w;
+ init.display.desired_windowed_height = screen->h;
+ resize(init.display.desired_fullscreen_width,
+ init.display.desired_fullscreen_height);
+ } else {
+ resize(init.display.desired_windowed_width, init.display.desired_windowed_height);
+ }
+ }
+};
+
+// Specialization for PARTIAL:0
+class renderer_once : public renderer_opengl {
+ int tile_count;
+
+protected:
+ void update_tile(int x, int y) {
+ write_tile_vertexes(x, y, vertexes + tile_count * 6 * 2);
+ write_tile_arrays(x, y,
+ fg + tile_count * 6 * 4,
+ bg + tile_count * 6 * 4,
+ tex + tile_count * 6 * 2);
+ tile_count++;
+ }
+
+ void draw(int dummy) {
+ renderer_opengl::draw(tile_count*6);
+ tile_count = 0;
+ }
+
+public:
+ renderer_once() {
+ tile_count = 0;
+ }
+};
+
+// PARTIAL:N
+class renderer_partial : public renderer_opengl {
+ int buffersz;
+ list<int> erasz; // Previous eras
+ int current_erasz; // And the current one
+ int sum_erasz;
+ int head, tail; // First unused tile, first used tile respectively
+ int redraw_count; // Number of eras to max out at
+
+ void update_tile(int x, int y) {
+ write_tile_vertexes(x, y, vertexes + head * 6 * 2);
+ write_tile_arrays(x, y,
+ fg + head * 6 * 4,
+ bg + head * 6 * 4,
+ tex + head * 6 * 2);
+ head = (head + 1) % buffersz;
+ current_erasz++; sum_erasz++;
+ if (head == tail) {
+ //gamelog << "Expanding partial-printing buffer" << endl;
+ // Buffer is full, expand it.
+ renderer_opengl::allocate(buffersz * 2);
+ // Move the tail to the end of the newly allocated space
+ tail += buffersz;
+ memmove(vertexes + tail * 6 * 2, vertexes + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head));
+ memmove(fg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head));
+ memmove(bg + tail * 6 * 4, fg + head * 6 * 4, sizeof(GLfloat) * 6 * 4 * (buffersz - head));
+ memmove(tex + tail * 6 * 2, fg + head * 6 * 2, sizeof(GLfloat) * 6 * 2 * (buffersz - head));
+ // And finish.
+ buffersz *= 2;
+ }
+ }
+
+ void allocate(int tile_count) {
+ assert(false);
+ }
+
+ virtual void reshape_gl() {
+ // TODO: This function is duplicate code w/base class reshape_gl
+ // Setup invariant state
+ glEnableClientState(GL_COLOR_ARRAY);
+ /// Set up our coordinate system
+ if (forced_steps + zoom_steps == 0 &&
+ init.display.flag.has_flag(INIT_DISPLAY_FLAG_BLACK_SPACE)) {
+ size_x = gps.dimx * dispx;
+ size_y = gps.dimy * dispy;
+ off_x = (screen->w - size_x) / 2;
+ off_y = (screen->h - size_y) / 2;
+ } else {
+ // If we're zooming (or just not using black space), we use the
+ // entire window.
+ size_x = screen->w;
+ size_y = screen->h;
+ off_x = off_y = 0;
+ }
+ glViewport(off_x, off_y, size_x, size_y);
+ glMatrixMode(GL_MODELVIEW);
+ glLoadIdentity();
+ glMatrixMode(GL_PROJECTION);
+ glLoadIdentity();
+ gluOrtho2D(0, gps.dimx, gps.dimy, 0);
+ }
+
+ void draw_arrays(GLfloat *vertexes, GLfloat *fg, GLfloat *bg, GLfloat *tex, int tile_count) {
+ // Set vertex pointer
+ glVertexPointer(2, GL_FLOAT, 0, vertexes);
+ // Render the background colors
+ glDisable(GL_TEXTURE_2D);
+ glDisableClientState(GL_TEXTURE_COORD_ARRAY);
+ glDisable(GL_BLEND);
+ glDisable(GL_ALPHA_TEST);
+ glColorPointer(4, GL_FLOAT, 0, bg);
+ glDrawArrays(GL_TRIANGLES, 0, tile_count * 6);
+ // Render the foreground, colors and textures both
+ glEnable(GL_ALPHA_TEST);
+ glAlphaFunc(GL_NOTEQUAL, 0);
+ glEnable(GL_TEXTURE_2D);
+ glEnableClientState(GL_TEXTURE_COORD_ARRAY);
+ glEnable(GL_BLEND);
+ glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
+ glColorPointer(4, GL_FLOAT, 0, fg);
+ glTexCoordPointer(2, GL_FLOAT, 0, tex);
+ glDrawArrays(GL_TRIANGLES, 0, tile_count * 6);
+ }
+
+ void draw(int dummy) {
+ if (tail > head) {
+ // We're straddling the end of the array, so have to do this in two steps
+ draw_arrays(vertexes + tail * 6 * 2,
+ fg + tail * 6 * 4,
+ bg + tail * 6 * 4,
+ tex + tail * 6 * 2,
+ buffersz - tail);
+ draw_arrays(vertexes, fg, bg, tex, head-1);
+ } else {
+ draw_arrays(vertexes + tail * 6 * 2,
+ fg + tail * 6 * 4,
+ bg + tail * 6 * 4,
+ tex + tail * 6 * 2,
+ sum_erasz);
+ }
+
+ printGLError();
+ erasz.push_back(current_erasz); current_erasz = 0;
+ if (erasz.size() == redraw_count) {
+ // Right, time to retire the oldest era.
+ tail = (tail + erasz.front()) % buffersz;
+ sum_erasz -= erasz.front();
+ erasz.pop_front();
+ }
+ }
+
+public:
+ renderer_partial() {
+ redraw_count = init.display.partial_print_count;
+ buffersz = 2048;
+ renderer_opengl::allocate(buffersz);
+ current_erasz = head = tail = sum_erasz = 0;
+ }
+};
+
+class renderer_accum_buffer : public renderer_once {
+ void draw(int vertex_count) {
+ // Copy the previous frame's buffer back in
+ glAccum(GL_RETURN, 1);
+ renderer_once::draw(vertex_count);
+ // Store the screen contents back to the buffer
+ glAccum(GL_LOAD, 1);
+ }
+};
+
+class renderer_framebuffer : public renderer_once {
+ GLuint framebuffer, fb_texture;
+
+ void init_opengl() {
+ glGenFramebuffersEXT(1, &framebuffer);
+ // Allocate FBO texture memory
+ glGenTextures(1, &fb_texture);
+ glBindTexture(GL_TEXTURE_2D, fb_texture);
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
+ screen->w, screen->h,
+ 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
+ glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
+
+ // Bind texture to FBO
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer);
+ glFramebufferTexture2DEXT(GL_DRAW_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT,
+ GL_TEXTURE_2D, fb_texture, 0);
+ renderer_once::init_opengl();
+ }
+
+ void uninit_opengl() {
+ renderer_once::uninit_opengl();
+ glDeleteTextures(1, &fb_texture);
+ glDeleteFramebuffersEXT(1, &framebuffer);
+ }
+
+ void draw(int vertex_count) {
+ // Bind the framebuffer
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, framebuffer);
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, 0);
+ // Draw
+ renderer_once::draw(vertex_count);
+ // Draw the framebuffer to screen
+ glBindFramebufferEXT(GL_DRAW_FRAMEBUFFER_EXT, 0);
+ glBindFramebufferEXT(GL_READ_FRAMEBUFFER_EXT, framebuffer);
+ glBlitFramebufferEXT(0,0, screen->w, screen->h,
+ 0,0, screen->w, screen->h,
+ GL_COLOR_BUFFER_BIT, GL_NEAREST);
+ printGLError();
+ }
+};
+
+class renderer_vbo : public renderer_opengl {
+ GLuint vbo; // Vertexes only
+
+ void init_opengl() {
+ renderer_opengl::init_opengl();
+ glGenBuffersARB(1, &vbo);
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, vbo);
+ glBufferDataARB(GL_ARRAY_BUFFER_ARB, gps.dimx*gps.dimy*6*2*sizeof(GLfloat), vertexes, GL_STATIC_DRAW_ARB);
+ glVertexPointer(2, GL_FLOAT, 0, 0);
+ glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
+ }
+
+ void uninit_opengl() {
+ glDeleteBuffersARB(1, &vbo);
+ renderer_opengl::uninit_opengl();
+ }
+};