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

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