Merge branch terrible and branch awful
[ipdf/code.git] / src / screen.cpp
1 #include "common.h"
2 #include "screen.h"
3
4 #include "gl_core44.h"
5 #include <fcntl.h> // for access(2)
6 #include <unistd.h> // for access(2)
7
8 #include "bufferbuilder.h"
9 #include "shaderprogram.h"
10
11
12
13 #define BASICTEX_VERT "shaders/basictex_vert.glsl"
14 #define BASICTEX_FRAG "shaders/basictex_frag.glsl"
15
16 using namespace IPDF;
17 using namespace std;
18
19 #ifndef __MINGW32__
20 static void opengl_debug_callback(GLenum source, GLenum type, GLuint id, GLenum severity, GLsizei length, const GLchar* msg, const void *data)
21 {
22         // Don't print out gl Errors we generated.
23         if (source == GL_DEBUG_SOURCE_APPLICATION) return;
24         //Error("OpenGL Error (%d): %s", id, msg);
25         // Spams this message on fglrx, disabling for now because it's damn annoying.
26         // ERROR: opengl_debug_callback (screen.cpp:21) - OpenGL Error (1011): glObjectLabel failed because (depending on the operation) a referenced binding point is empty; a referenced name is not the name of an object; or the given name is otherwise not valid to this operation (GL_INVALID_VALUE)
27 }
28 #endif
29
30 Screen::Screen(bool visible)
31 {
32
33         SDL_Init(SDL_INIT_VIDEO);
34         uint32_t flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE;
35         if (!visible)
36                 flags |= SDL_WINDOW_HIDDEN;
37         
38         m_window = SDL_CreateWindow("IPDF", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
39                         800, 600, flags);
40
41         if (!m_window)
42         {
43                 Error("Couldn't create window!");
44                 return;
45         }
46
47         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
48         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
49         SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
50         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
51         //SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
52         m_gl_context = SDL_GL_CreateContext(m_window);
53         //SDL_GL_SetSwapInterval(1);
54
55         ogl_LoadFunctions();
56
57         // Why is this so horribly broken?
58         if (ogl_IsVersionGEQ(3,2))
59         {
60                 Error("We require OpenGL 3.3, but you have version %d.%d!",ogl_GetMajorVersion(), ogl_GetMinorVersion());
61         }
62
63         if (!SDL_GL_ExtensionSupported("GL_ARB_shading_language_420pack"))
64         {
65                 Error("Your system does not support the ARB_shading_language_420pack extension, which is required.");
66         }
67
68         if (!SDL_GL_ExtensionSupported("GL_ARB_explicit_attrib_location"))
69         {
70                 Error("Your system does not support the ARB_explicit_attrib_location extension, which is required.");
71         }
72
73         m_frame_begin_time = SDL_GetPerformanceCounter();
74         m_last_frame_time = 0;
75         m_last_frame_gpu_timer = 0;
76
77         glGenQueries(1, &m_frame_gpu_timer);
78         glBeginQuery(GL_TIME_ELAPSED, m_frame_gpu_timer);
79         #ifndef __MINGW32__
80         glDebugMessageCallback(opengl_debug_callback, 0);
81         #endif
82
83         GLuint default_vao;
84         glGenVertexArrays(1, &default_vao);
85         glBindVertexArray(default_vao);
86
87         m_texture_prog.InitialiseShaders(BASICTEX_VERT, BASICTEX_FRAG);
88         m_texture_prog.Use();
89
90         // We always want to use the texture bound to texture unit 0.
91         GLint texture_uniform_location = m_texture_prog.GetUniformLocation("tex");
92         glUniform1i(texture_uniform_location, 0);
93
94         m_font_prog.InitialiseShaders(BASICTEX_VERT, "shaders/fonttex_frag.glsl");
95         m_font_prog.Use();
96
97         // We always want to use the texture bound to texture unit 0.
98         GLint font_texture_uniform_location = m_font_prog.GetUniformLocation("tex");
99         glUniform1i(font_texture_uniform_location, 0);
100         m_colour_uniform_location = m_font_prog.GetUniformLocation("colour");
101
102         m_viewport_ubo.SetUsage(GraphicsBuffer::BufferUsageDynamicDraw);
103         m_viewport_ubo.SetType(GraphicsBuffer::BufferTypeUniform);
104
105         m_debug_font_atlas = 0;
106         m_no_quit_requested = true;
107         m_show_debug_font = true;
108         m_view = NULL;
109         ResizeViewport(800, 600);
110         
111         
112         
113         Clear();
114         Present();
115         
116
117 }
118
119 Screen::~Screen()
120 {
121         if (!Valid())
122                 return;
123         SDL_GL_DeleteContext(m_gl_context);
124         SDL_DestroyWindow(m_window);
125         SDL_Quit();
126 }
127
128 void Screen::Clear(float r, float g, float b, float a)
129 {
130         if (!Valid())
131                 return;
132         glClearColor(r,g,b,a);
133         glClear(GL_COLOR_BUFFER_BIT);
134         DebugFontClear();
135 }
136
137 void Screen::ResizeViewport(int width, int height)
138 {
139         glViewport(0, 0, width, height);
140         m_viewport_width = width;
141         m_viewport_height = height;
142         GLfloat viewportfloats[] = {(float)width, (float)height};
143         m_viewport_ubo.Upload(sizeof(float)*2, viewportfloats);
144 }
145
146 bool Screen::PumpEvents()
147 {
148         if (!Valid())
149                 return true;
150
151         SDL_Event evt;
152         
153         while (SDL_PollEvent(&evt))
154         {
155                 switch (evt.type)
156                 {
157                 case SDL_QUIT:
158                         m_no_quit_requested = false;
159                         break;
160                 case SDL_WINDOWEVENT:
161                         switch (evt.window.event)
162                         {
163                         case SDL_WINDOWEVENT_RESIZED:
164                         case SDL_WINDOWEVENT_SIZE_CHANGED:
165                                 ResizeViewport(evt.window.data1, evt.window.data2);
166                                 break;
167                         }
168                         break;
169                 case SDL_MOUSEMOTION:
170                         m_last_mouse_x = evt.motion.x;
171                         m_last_mouse_y = evt.motion.y;
172                         if (m_mouse_handler)
173                         {
174                                 m_mouse_handler(evt.motion.x, evt.motion.y,evt.motion.state, 0, this, m_view);
175                         }
176                         break;
177                 case SDL_MOUSEBUTTONDOWN:
178                 case SDL_MOUSEBUTTONUP:
179                         m_last_mouse_x = evt.button.x;
180                         m_last_mouse_y = evt.button.y;
181                         if (m_mouse_handler)
182                         {
183                                 m_mouse_handler(evt.button.x, evt.button.y, evt.button.state?evt.button.button:0, 0, this, m_view);
184                         }
185                         break;
186                 case SDL_MOUSEWHEEL:
187                         if (m_mouse_handler)
188                         {
189                                 m_mouse_handler(m_last_mouse_x, m_last_mouse_y, 0, evt.wheel.y, this, m_view);
190                         }
191                         break;
192                 case SDL_KEYDOWN:
193                 {
194                         Debug("Key %c down", (char)evt.key.keysym.sym);
195                         if (isalnum((char)evt.key.keysym.sym))
196                         {
197                                 char filename[] = "0.bmp";
198                                 filename[0] = (char)evt.key.keysym.sym;
199                                 ScreenShot(filename);
200                         }
201                         break;
202                 }
203                 default:
204                         break;
205                 }
206         }
207         return m_no_quit_requested;
208 }
209
210 void Screen::SetMouseCursor(Screen::MouseCursors cursor)
211 {
212         SDL_SystemCursor system_cursor_id = SDL_SYSTEM_CURSOR_ARROW;
213         switch (cursor)
214         {
215         case CursorArrow: system_cursor_id = SDL_SYSTEM_CURSOR_ARROW; break;
216         case CursorWait: system_cursor_id = SDL_SYSTEM_CURSOR_WAIT; break;
217         case CursorWaitArrow: system_cursor_id = SDL_SYSTEM_CURSOR_WAITARROW; break;
218         case CursorMove: system_cursor_id = SDL_SYSTEM_CURSOR_SIZEALL; break;
219         case CursorHand: system_cursor_id = SDL_SYSTEM_CURSOR_HAND; break;
220         default: break;
221         }
222         SDL_Cursor *system_cursor = SDL_CreateSystemCursor(system_cursor_id);
223         SDL_SetCursor(system_cursor);
224         //TODO: Check if we need to free the system cursors.
225 }
226
227 void Screen::Present()
228 {
229         if (!Valid())
230                 return;
231
232         if (m_debug_font_atlas)
233                 DebugFontFlush();
234
235         m_last_frame_time = SDL_GetPerformanceCounter() - m_frame_begin_time;
236         glEndQuery(GL_TIME_ELAPSED);
237         SDL_GL_SwapWindow(m_window);
238         m_frame_begin_time = SDL_GetPerformanceCounter();
239         if (m_last_frame_gpu_timer)
240                 glDeleteQueries(1, &m_last_frame_gpu_timer);
241         m_last_frame_gpu_timer = m_frame_gpu_timer;
242         glGenQueries(1, &m_frame_gpu_timer);
243         glBeginQuery(GL_TIME_ELAPSED, m_frame_gpu_timer);
244
245
246 }
247
248 double Screen::GetLastFrameTimeGPU() const
249 {
250         if (!Valid() || !m_last_frame_gpu_timer)
251                 return 0;
252         uint64_t frame_time_ns;
253         glGetQueryObjectui64v(m_last_frame_gpu_timer, GL_QUERY_RESULT, &frame_time_ns);
254         return frame_time_ns/1000000000.0;
255 }
256
257 void Screen::RenderPixels(int x, int y, int w, int h, uint8_t *pixels) const
258 {
259         GLenum texture_format = GL_RGBA;
260
261         m_texture_prog.Use();
262         GraphicsBuffer quad_vertex_buffer;
263         quad_vertex_buffer.SetUsage(GraphicsBuffer::BufferUsageStaticDraw);
264         quad_vertex_buffer.SetType(GraphicsBuffer::BufferTypeVertex);
265         //rectangular texture == 2 triangles
266         GLfloat quad[] = { 
267                 0, 0, (float)x, (float)y,
268                 1, 0, (float)(x+w), (float)y,
269                 0, 1, (float)x, (float)(y+h),
270                 1, 1, (float)(x+w), (float)(y+h)
271         };
272         quad_vertex_buffer.Upload(sizeof(GLfloat) * 16, quad);
273         quad_vertex_buffer.Bind();
274         m_viewport_ubo.Bind();
275
276         glUniform4f(m_colour_uniform_location, 1.0f, 1.0f, 1.0f, 1.0f);
277
278         GLuint texID;
279         glGenTextures(1, &texID);
280         glBindTexture(GL_TEXTURE_2D, texID);
281
282         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
283         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
284         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
285         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
286
287         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, texture_format, GL_UNSIGNED_BYTE, pixels);
288
289         glEnableVertexAttribArray(0);
290         glEnableVertexAttribArray(1);
291         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(2*sizeof(float)));
292         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
293         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
294         glDisableVertexAttribArray(1);
295         glDisableVertexAttribArray(0);
296
297 }
298
299 void Screen::ScreenShot(const char * filename) const
300 {
301         if (!Valid()) return;
302         Debug("Attempting to save BMP to file %s", filename);
303
304         int w = ViewportWidth();
305         int h = ViewportHeight();
306         unsigned char * pixels = new unsigned char[w*h*4];
307         if (pixels == NULL)
308                 Fatal("Failed to allocate %d x %d x 4 = %d pixel array", w, h, w*h*4);
309
310         for (int y = 0; y < h; ++y)
311         {
312                 glReadPixels(0,h-y-1,w, 1, GL_RGBA, GL_UNSIGNED_BYTE, &pixels[y*w*4]);
313         }
314
315 #if SDL_BYTEORDER == SDL_LIL_ENDIAN
316         SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(pixels, w, h, 8*4, w*4, 0x000000ff,0x0000ff00,0x00ff0000,0xff000000);
317 #else
318         SDL_Surface * surf = SDL_CreateRGBSurfaceFrom(pixels, w, h, 8*4, w*4, 0xff000000,0x00ff0000,0x0000ff00,0x000000ff);
319 #endif
320         if (surf == NULL)
321                 Fatal("Failed to create SDL_Surface from pixel data - %s", SDL_GetError());
322
323         GLenum texture_format = (surf->format->Rmask == 0x000000FF) ? GL_RGBA : GL_BGRA;
324         Debug("SDL_Surface %d BytesPerPixel, format %d (RGB = %d, BGR = %d, RGBA = %d, BGRA = %d)", surf->format->BytesPerPixel, texture_format, GL_RGB, GL_BGR, GL_RGBA, GL_BGRA);
325
326         if (SDL_SaveBMP(surf, filename) != 0)
327                 Fatal("SDL_SaveBMP failed - %s", SDL_GetError());
328         
329         SDL_FreeSurface(surf);
330         delete [] pixels;
331         Debug("Succeeded!");
332 }
333
334 /**
335  * Render a BMP
336  * NOT PART OF THE DOCUMENT FORMAT
337  */
338 void Screen::RenderBMP(const char * filename) const
339 {
340         if (!Valid()) return;
341         if (access(filename, R_OK) == -1)
342         {
343                 Error("No such file \"%s\" - Nothing to render - You might have done this deliberately?", filename);
344                 return;
345         }
346         SDL_Surface * bmp = SDL_LoadBMP(filename);
347         if (bmp == NULL)
348                 Fatal("Failed to load BMP from %s - %s", filename, SDL_GetError());
349
350         int w = bmp->w;
351         int h = bmp->h;
352
353         GLenum texture_format = GL_RGBA;
354         switch (bmp->format->BytesPerPixel)
355         {
356                 case 4: //contains alpha
357                         texture_format = (bmp->format->Rmask == 0x000000FF) ? GL_RGBA : GL_BGRA;
358                         break;
359                 case 3: //does not contain alpha
360                         texture_format = (bmp->format->Rmask == 0x000000FF) ? GL_RGB : GL_BGR;  
361                         break;
362                 default:
363                         Fatal("Could not understand SDL_Surface format (%d colours)", bmp->format->BytesPerPixel);
364                         break;  
365         }
366
367         //Debug("SDL_Surface %d BytesPerPixel, format %d (RGB = %d, BGR = %d, RGBA = %d, BGRA = %d)", bmp->format->BytesPerPixel, texture_format, GL_RGB, GL_BGR, GL_RGBA, GL_BGRA);
368
369         m_texture_prog.Use();
370         GraphicsBuffer quad_vertex_buffer;
371         quad_vertex_buffer.SetUsage(GraphicsBuffer::BufferUsageStaticDraw);
372         quad_vertex_buffer.SetType(GraphicsBuffer::BufferTypeVertex);
373         GLfloat quad[] = { 
374                 0, 0, 0, 0,
375                 1, 0, (float)ViewportWidth(), 0,
376                 1, 1, (float)ViewportWidth(), (float)ViewportHeight(),
377                 0, 1, 0, (float)ViewportHeight()
378         };
379         quad_vertex_buffer.Upload(sizeof(GLfloat) * 16, quad);
380         quad_vertex_buffer.Bind();
381         m_viewport_ubo.Bind();
382
383         glUniform4f(m_colour_uniform_location, 1.0f, 1.0f, 1.0f, 1.0f);
384
385         GLuint texID;
386         glGenTextures(1, &texID);
387         glBindTexture(GL_TEXTURE_2D, texID);
388
389         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
390         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
391         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
392         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
393
394         glTexImage2D(GL_TEXTURE_2D, 0, bmp->format->BytesPerPixel, w, h, 0, texture_format, GL_UNSIGNED_BYTE, bmp->pixels);
395
396         glEnableVertexAttribArray(0);
397         glEnableVertexAttribArray(1);
398         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(2*sizeof(float)));
399         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
400         glDisableVertexAttribArray(1);
401         glDisableVertexAttribArray(0);
402         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
403
404         SDL_FreeSurface(bmp);   
405 }
406
407 void Screen::DebugFontInit(const char *name, float font_size)
408 {
409         if (!Valid()) return;
410
411         unsigned char font_atlas_data[1024*1024];
412         FILE *font_file = fopen(name, "rb");
413         fseek(font_file, 0, SEEK_END);
414         size_t font_file_size = ftell(font_file);
415         fseek(font_file, 0, SEEK_SET);
416         unsigned char *font_file_data = (unsigned char*)malloc(font_file_size);
417         SDL_assert(fread(font_file_data, 1, font_file_size, font_file) == font_file_size);
418         fclose(font_file);
419         stbtt_BakeFontBitmap(font_file_data,0, font_size, font_atlas_data,1024,1024, 32,96, m_debug_font_rects);
420         free(font_file_data);
421         glGenTextures(1, &m_debug_font_atlas);
422         glBindTexture(GL_TEXTURE_2D, m_debug_font_atlas);
423         glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, 1024,1024, 0, GL_RED, GL_UNSIGNED_BYTE, font_atlas_data);
424         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
425         m_debug_font_size = font_size;
426
427         m_debug_font_vertices.SetUsage(GraphicsBuffer::BufferUsageStreamDraw);
428         m_debug_font_vertices.SetType(GraphicsBuffer::BufferTypeVertex);
429         m_debug_font_vertices.SetName("m_debug_font_vertices");
430         m_debug_font_vertices.Upload(8192,NULL);
431         m_debug_font_vertex_head = 0;
432
433         m_debug_font_indices.SetUsage(GraphicsBuffer::BufferUsageStreamDraw);
434         m_debug_font_indices.SetType(GraphicsBuffer::BufferTypeIndex);
435         m_debug_font_indices.SetName("m_debug_font_indices");
436         m_debug_font_indices.Resize(500);
437         m_debug_font_index_head = 0;
438 }
439
440 void Screen::DebugFontClear()
441 {
442         if (!Valid()) return;
443         m_debug_font_x = m_debug_font_y = 0;
444         if (!m_debug_font_atlas) return;
445         DebugFontPrint("\n");
446 }
447
448 void Screen::DebugFontFlush()
449 {
450         if (!Valid()) return;
451         glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 40, -1, "Screen::DebugFontFlush()");      
452                 
453         glEnable(GL_BLEND);
454         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
455         glBindTexture(GL_TEXTURE_2D, m_debug_font_atlas);
456
457         m_font_prog.Use();
458         m_viewport_ubo.Bind();
459         m_debug_font_vertices.Bind();
460         m_debug_font_indices.Bind();
461         glUniform4f(m_colour_uniform_location, 0,0,0,1);
462         glEnableVertexAttribArray(0);
463         glEnableVertexAttribArray(1);
464         glEnable(GL_PRIMITIVE_RESTART);
465         glPrimitiveRestartIndex(65535);
466         glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), 0);
467         glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4*sizeof(float), (void*)(2*sizeof(float)));
468         glDrawElements(GL_TRIANGLE_STRIP, m_debug_font_index_head, GL_UNSIGNED_SHORT, 0);
469         glDisable(GL_PRIMITIVE_RESTART);
470         glDisableVertexAttribArray(1);
471         glDisableVertexAttribArray(0);
472
473         glDisable(GL_BLEND);
474
475         m_debug_font_vertices.Invalidate();
476         m_debug_font_indices.Invalidate();
477         m_debug_font_vertex_head = 0;
478         m_debug_font_index_head = 0;
479
480         glPopDebugGroup();
481 }
482
483 struct fontvertex
484 {
485         float x, y, s, t;
486 };
487
488 void Screen::DebugFontPrint(const char* str)
489 {
490         if (!Valid()) return;
491         if (!m_debug_font_atlas || !m_show_debug_font) return;
492
493         glPushDebugGroup(GL_DEBUG_SOURCE_APPLICATION, 41, -1, "Screen::DebugFontPrint()");
494
495         BufferBuilder<fontvertex> vertexData(m_debug_font_vertices.MapRange(m_debug_font_vertex_head*sizeof(float), m_debug_font_vertices.GetSize() - m_debug_font_vertex_head*sizeof(float), false, true, true), m_debug_font_vertices.GetSize() - m_debug_font_vertex_head*sizeof(float));
496         BufferBuilder<uint16_t> indexData(m_debug_font_indices.MapRange(m_debug_font_index_head*sizeof(uint16_t), m_debug_font_indices.GetSize() - m_debug_font_index_head*sizeof(uint16_t), false, true, true), m_debug_font_indices.GetSize() - m_debug_font_index_head*sizeof(uint16_t));
497
498         size_t baseVertex = m_debug_font_vertex_head/4;
499         while (*str) {
500                 if (!vertexData.Free(4) || !indexData.Free(5))
501                 {
502                         m_debug_font_indices.UnMap();
503                         m_debug_font_vertices.UnMap();
504                         DebugFontFlush();
505                         DebugFontPrint(str);
506                         glPopDebugGroup();
507                         return;
508                 }
509                 if (*str >= 32 && (unsigned char)(*str) < 128) {
510                         stbtt_aligned_quad q;
511                         stbtt_GetBakedQuad(m_debug_font_rects, 1024,1024, *str-32, &m_debug_font_x,&m_debug_font_y,&q,1);
512                         size_t index = vertexData.Add({q.x0, q.y0, q.s0, q.t0});
513                         index += baseVertex;
514                         indexData.Add(index);
515                         index = vertexData.Add({q.x1, q.y0, q.s1, q.t0});
516                         index += baseVertex;
517                         indexData.Add(index);
518                         index = vertexData.Add({q.x0, q.y1, q.s0, q.t1});
519                         index += baseVertex;
520                         indexData.Add(index);
521                         index = vertexData.Add({q.x1, q.y1, q.s1, q.t1});
522                         index += baseVertex;
523                         indexData.Add(index);
524                         indexData.Add(65535);
525
526                         m_debug_font_vertex_head += 16;
527                         m_debug_font_index_head += 5;
528
529                 }
530                 else if (*str == '\n')
531                 {
532                         m_debug_font_x = 0;
533                         m_debug_font_y += m_debug_font_size;
534                 }
535                 ++str;
536         }
537         m_debug_font_indices.UnMap();
538         m_debug_font_vertices.UnMap();
539         glPopDebugGroup();
540         //DebugFontFlush();
541 }
542
543 void Screen::DebugFontPrintF(const char *fmt, ...)
544 {
545         char buffer[BUFSIZ];
546         va_list va;
547         va_start(va, fmt);
548         vsnprintf(buffer, BUFSIZ, fmt,va);
549         va_end(va);
550         DebugFontPrint(buffer);
551 }

UCC git Repository :: git.ucc.asn.au