]> icculus.org git repositories - taylor/freespace2.git/blob - src/graphics/grgl2.cpp
implement GL2 framebuffer, plus various fixes
[taylor/freespace2.git] / src / graphics / grgl2.cpp
1 /*
2  * Copyright (C) Volition, Inc. 1999.  All rights reserved.
3  *
4  * All source code herein is the property of Volition, Inc. You may not sell
5  * or otherwise commercially exploit the source or things you created based on
6  * the source.
7  */
8
9 #include "SDL_opengles2.h"
10
11 #include "gropengl.h"
12 #include "gropenglinternal.h"
13 #include "grgl2.h"
14 #include "2d.h"
15 #include "mouse.h"
16 #include "pstypes.h"
17 #include "cfile.h"
18 #include "bmpman.h"
19 #include "grinternal.h"
20 #include "osapi.h"
21 #include "osregistry.h"
22
23
24 int GL_two_inited = 0;
25
26 bool Use_mipmaps = false;
27
28 static GLuint FB_texture = 0;
29 static GLuint FB_id = 0;
30 static GLuint FB_rb_id = 0;
31
32 static GLuint GL_saved_screen_tex = 0;
33
34
35 static gr_alpha_blend GL_current_alpha_blend = (gr_alpha_blend) -1;
36 static gr_zbuffer_type GL_current_zbuffer_type = (gr_zbuffer_type) -1;
37
38 void opengl2_set_state(gr_texture_source ts, gr_alpha_blend ab, gr_zbuffer_type zt)
39 {
40         opengl2_set_texture_state(ts);
41
42         if (ab != GL_current_alpha_blend) {
43                 switch (ab) {
44                         case ALPHA_BLEND_NONE:                  // 1*SrcPixel + 0*DestPixel
45                                 glBlendFunc(GL_ONE, GL_ZERO);
46                                 break;
47                         case ALPHA_BLEND_ADDITIVE:              // 1*SrcPixel + 1*DestPixel
48                                 glBlendFunc(GL_ONE, GL_ONE);
49                                 break;
50                         case ALPHA_BLEND_ALPHA_ADDITIVE:        // Alpha*SrcPixel + 1*DestPixel
51                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE);
52                                 break;
53                         case ALPHA_BLEND_ALPHA_BLEND_ALPHA:     // Alpha*SrcPixel + (1-Alpha)*DestPixel
54                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
55                                 break;
56                         case ALPHA_BLEND_ALPHA_BLEND_SRC_COLOR: // Alpha*SrcPixel + (1-SrcPixel)*DestPixel
57                                 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_COLOR);
58                                 break;
59                         default:
60                                 break;
61                 }
62
63                 GL_current_alpha_blend = ab;
64         }
65
66         if (zt != GL_current_zbuffer_type) {
67                 switch (zt) {
68                         case ZBUFFER_TYPE_NONE:
69                                 glDepthFunc(GL_ALWAYS);
70                                 glDepthMask(GL_FALSE);
71                                 break;
72                         case ZBUFFER_TYPE_READ:
73                                 glDepthFunc(GL_LESS);
74                                 glDepthMask(GL_FALSE);
75                                 break;
76                         case ZBUFFER_TYPE_WRITE:
77                                 glDepthFunc(GL_ALWAYS);
78                                 glDepthMask(GL_TRUE);
79                                 break;
80                         case ZBUFFER_TYPE_FULL:
81                                 glDepthFunc(GL_LESS);
82                                 glDepthMask(GL_TRUE);
83                                 break;
84                         default:
85                                 break;
86                 }
87
88                 GL_current_zbuffer_type = zt;
89         }
90 }
91
92 static void opengl2_init_func_pointers()
93 {
94         gr_screen.gf_flip = gr_opengl2_flip;
95         gr_screen.gf_set_clip = gr_opengl2_set_clip;
96         gr_screen.gf_reset_clip = gr_opengl_reset_clip;
97
98         gr_screen.gf_clear = gr_opengl_clear;
99
100         gr_screen.gf_aabitmap = gr_opengl2_aabitmap;
101         gr_screen.gf_aabitmap_ex = gr_opengl2_aabitmap_ex;
102
103         gr_screen.gf_rect = gr_opengl2_rect;
104         gr_screen.gf_shade = gr_opengl2_shade;
105         gr_screen.gf_string = gr_opengl2_string;
106         gr_screen.gf_circle = gr_opengl2_circle;
107
108         gr_screen.gf_line = gr_opengl2_line;
109         gr_screen.gf_aaline = gr_opengl2_aaline;
110         gr_screen.gf_pixel = gr_opengl2_pixel;
111         gr_screen.gf_scaler = gr_opengl2_scaler;
112         gr_screen.gf_tmapper = gr_opengl2_tmapper;
113
114         gr_screen.gf_gradient = gr_opengl2_gradient;
115
116         gr_screen.gf_print_screen = gr_opengl_print_screen;
117
118         gr_screen.gf_fade_in = gr_opengl2_fade_in;
119         gr_screen.gf_fade_out = gr_opengl2_fade_out;
120         gr_screen.gf_flash = gr_opengl2_flash;
121
122         gr_screen.gf_zbuffer_clear = gr_opengl2_zbuffer_clear;
123
124         gr_screen.gf_save_screen = gr_opengl2_save_screen;
125         gr_screen.gf_restore_screen = gr_opengl2_restore_screen;
126         gr_screen.gf_free_screen = gr_opengl2_free_screen;
127
128         gr_screen.gf_dump_frame_start = gr_opengl2_dump_frame_start;
129         gr_screen.gf_dump_frame_stop = gr_opengl2_dump_frame_stop;
130         gr_screen.gf_dump_frame = gr_opengl2_dump_frame;
131
132         gr_screen.gf_set_gamma = gr_opengl2_set_gamma;
133
134         gr_screen.gf_lock = gr_opengl_lock;
135         gr_screen.gf_unlock = gr_opengl_unlock;
136
137         gr_screen.gf_fog_set = gr_opengl2_fog_set;
138
139         gr_screen.gf_get_region = gr_opengl2_get_region;
140
141         gr_screen.gf_set_cull = gr_opengl_set_cull;
142
143         gr_screen.gf_cross_fade = gr_opengl2_cross_fade;
144
145         gr_screen.gf_preload_init = gr_opengl2_preload_init;
146         gr_screen.gf_preload = gr_opengl2_preload;
147
148         gr_screen.gf_zbias = gr_opengl_zbias;
149
150         gr_screen.gf_force_windowed = gr_opengl_force_windowed;
151         gr_screen.gf_force_fullscreen = gr_opengl_force_fullscreen;
152         gr_screen.gf_toggle_fullscreen = gr_opengl_toggle_fullscreen;
153
154         gr_screen.gf_set_viewport = gr_opengl2_set_viewport;
155
156         gr_screen.gf_activate = gr_opengl_activate;
157
158         gr_screen.gf_release_texture = gr_opengl2_release_texture;
159 }
160
161 static int opengl2_create_framebuffer()
162 {
163         // create texture
164         glGenTextures(1, &FB_texture);
165         glBindTexture(GL_TEXTURE_2D, FB_texture);
166
167         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
168         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
169         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
170         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
171
172         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gr_screen.max_w, gr_screen.max_h, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
173
174         glBindTexture(GL_TEXTURE_2D, 0);
175
176         // create renderbuffer
177         glGenRenderbuffers(1, &FB_rb_id);
178         glBindRenderbuffer(GL_RENDERBUFFER, FB_rb_id);
179
180         glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, gr_screen.max_w, gr_screen.max_h);
181
182         // create framebuffer
183         glGenFramebuffers(1, &FB_id);
184         glBindFramebuffer(GL_FRAMEBUFFER, FB_id);
185
186         // attach texture and renderbuffer
187         glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, FB_texture, 0);
188         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, FB_rb_id);
189
190         GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
191
192         glBindFramebuffer(GL_FRAMEBUFFER, 0);
193
194         if (status != GL_FRAMEBUFFER_COMPLETE) {
195                 glBindFramebuffer(GL_FRAMEBUFFER, 0);
196
197                 if (FB_texture) {
198                         glDeleteTextures(1, &FB_texture);
199                         FB_texture = 0;
200                 }
201
202                 if (FB_rb_id) {
203                         glDeleteRenderbuffers(1, &FB_rb_id);
204                         FB_rb_id = 0;
205                 }
206
207                 if (FB_id) {
208                         glDeleteFramebuffers(1, &FB_id);
209                         FB_id = 0;
210                 }
211
212                 return 0;
213         }
214
215         return 1;
216 }
217
218 void opengl2_cleanup()
219 {
220         if ( !GL_two_inited ) {
221                 return;
222         }
223
224         glBindFramebuffer(GL_FRAMEBUFFER, 0);
225
226         if (FB_texture) {
227                 glDeleteTextures(1, &FB_texture);
228                 FB_texture = 0;
229         }
230
231         if (FB_rb_id) {
232                 glDeleteRenderbuffers(1, &FB_rb_id);
233                 FB_rb_id = 0;
234         }
235
236         if (FB_id) {
237                 glDeleteFramebuffers(1, &FB_id);
238                 FB_id = 0;
239         }
240
241         opengl2_tcache_cleanup();
242         opengl2_shader_cleanup();
243
244         if (GL_context) {
245                 SDL_GL_DeleteContext(GL_context);
246                 GL_context = NULL;
247         }
248
249         GL_two_inited = 0;
250 }
251
252 int opengl2_init()
253 {
254         if (GL_two_inited) {
255                 return 1;
256         }
257
258         GL_two_inited = 1;
259
260         SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
261         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2);
262         SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
263
264         GL_context = SDL_GL_CreateContext(GL_window);
265
266         if ( !GL_context ) {
267                 opengl2_cleanup();
268                 return 0;
269         }
270
271         mprintf(("  Vendor   : %s\n", glGetString(GL_VENDOR)));
272         mprintf(("  Renderer : %s\n", glGetString(GL_RENDERER)));
273         mprintf(("  Version  : %s\n", glGetString(GL_VERSION)));
274
275         // initial viewport setup
276         gr_opengl2_set_viewport(gr_screen.max_w, gr_screen.max_h);
277
278         // set up generic variables
279         opengl_set_variables();
280
281         opengl2_init_func_pointers();
282         opengl2_tcache_init();
283
284         if ( !opengl2_shader_init() ) {
285                 opengl2_cleanup();
286                 return 0;
287         }
288
289         if ( !opengl2_create_framebuffer() ) {
290                 opengl2_cleanup();
291                 return 0;
292         }
293
294         glEnable(GL_DITHER);
295         glEnable(GL_DEPTH_TEST);
296         glEnable(GL_BLEND);
297         glEnable(GL_TEXTURE_2D);
298
299         glDepthRangef(0.0f, 1.0f);
300
301         glPixelStorei(GL_PACK_ALIGNMENT, 1);
302         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
303
304         if ( SDL_GL_ExtensionSupported("GL_OES_texture_npot") ) {
305                 Use_mipmaps     = true;
306         }
307
308         mprintf(("  Mipmaps  : %s\n", Use_mipmaps ? "Enabled" : "Disabled"));
309
310         glFlush();
311
312         gr_opengl_clear();
313         gr_opengl_set_cull(1);
314
315         return 1;
316 }
317
318 void gr_opengl2_flip()
319 {
320         if ( !GL_two_inited ) {
321                 return;
322         }
323
324 #ifndef NDEBUG
325         static bool show_cursor = false;
326 #endif
327
328         glBindFramebuffer(GL_FRAMEBUFFER, 0);
329
330         gr_opengl_reset_clip();
331
332         // set viewport to window size
333         glViewport(GL_viewport_x, GL_viewport_y, GL_viewport_w, GL_viewport_h);
334
335         glClear(GL_COLOR_BUFFER_BIT);
336
337         {
338                 float x = 0.0f;
339                 float y = 0.0f;
340                 float w = i2fl(GL_viewport_w);
341                 float h = i2fl(GL_viewport_h);
342
343                 const float tex_coord[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f };
344                 const float ver_coord[] = { x, y, x, h, w, y, w, h };
345
346                 opengl2_shader_use(PROG_WINDOW);
347
348                 glEnableVertexAttribArray(SDRI_POSITION);
349                 glVertexAttribPointer(SDRI_POSITION, 2, GL_FLOAT, GL_FALSE, 0, &ver_coord);
350
351                 glEnableVertexAttribArray(SDRI_TEXCOORD);
352                 glVertexAttribPointer(SDRI_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, &tex_coord);
353
354                 glBindTexture(GL_TEXTURE_2D, FB_texture);
355
356                 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
357
358                 glBindTexture(GL_TEXTURE_2D, 0);
359
360                 glDisableVertexAttribArray(SDRI_TEXCOORD);
361                 glDisableVertexAttribArray(SDRI_POSITION);
362         }
363
364         mouse_eval_deltas();
365
366         if ( mouse_is_visible() ) {
367                 int mx, my;
368
369                 mouse_get_pos(&mx, &my);
370
371                 float u_scale, v_scale;
372
373                 if ( opengl2_tcache_set(Gr_cursor, TCACHE_TYPE_BITMAP_INTERFACE, &u_scale, &v_scale, 0) ) {
374                         opengl2_set_state(TEXTURE_SOURCE_DECAL, ALPHA_BLEND_ALPHA_BLEND_ALPHA, ZBUFFER_TYPE_NONE);
375
376                         int bw, bh;
377                         bm_get_info(Gr_cursor, &bw, &bh);
378
379                         float x = i2fl(mx) * GL_viewport_scale_w;
380                         float y = i2fl(my) * GL_viewport_scale_h;
381                         float w = x + (bw * GL_viewport_scale_w);
382                         float h = y + (bh * GL_viewport_scale_h);
383
384                         const float tex_coord[] = { 0.0f, 0.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f };
385                         const float ver_coord[] = { x, y, x, h, w, y, w, h };
386
387                         opengl2_shader_use(PROG_WINDOW);
388
389                         glEnableVertexAttribArray(SDRI_POSITION);
390                         glVertexAttribPointer(SDRI_POSITION, 2, GL_FLOAT, GL_FALSE, 0, &ver_coord);
391
392                         glEnableVertexAttribArray(SDRI_TEXCOORD);
393                         glVertexAttribPointer(SDRI_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, &tex_coord);
394
395                         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
396
397                         glDisableVertexAttribArray(SDRI_TEXCOORD);
398                         glDisableVertexAttribArray(SDRI_POSITION);
399                 }
400 #ifndef NDEBUG
401                 else if ( !show_cursor ) {
402                         SDL_ShowCursor(1);
403                         show_cursor = true;
404                 }
405 #endif
406         }
407
408 #ifndef NDEBUG
409         GLenum error = GL_NO_ERROR;
410
411         do {
412                 error = glGetError();
413
414                 if (error != GL_NO_ERROR) {
415                         nprintf(("Warning", "!!DEBUG!! OpenGL Error: %d\n", error));
416                 }
417         } while (error != GL_NO_ERROR);
418 #endif
419
420         SDL_GL_SwapWindow(GL_window);
421
422         opengl2_tcache_frame();
423
424         glBindFramebuffer(GL_FRAMEBUFFER, FB_id);
425
426         // set viewport to game screen size
427         glViewport(0, 0, gr_screen.max_w, gr_screen.max_h);
428 }
429
430 void gr_opengl2_set_clip(int x, int y, int w, int h)
431 {
432         // check for sanity of parameters
433         CAP(x, 0, gr_screen.max_w - 1);
434         CAP(y, 0, gr_screen.max_h - 1);
435         CAP(w, 0, gr_screen.max_w - x);
436         CAP(h, 0, gr_screen.max_h - y);
437
438         gr_screen.offset_x = x;
439         gr_screen.offset_y = y;
440         gr_screen.clip_left = 0;
441         gr_screen.clip_right = w-1;
442         gr_screen.clip_top = 0;
443         gr_screen.clip_bottom = h-1;
444         gr_screen.clip_width = w;
445         gr_screen.clip_height = h;
446
447         glEnable(GL_SCISSOR_TEST);
448         glScissor(x, gr_screen.max_h-y-h, w, h);
449 }
450
451 void gr_opengl2_fog_set(int fog_mode, int r, int g, int b, float fog_near, float fog_far)
452 {
453         gr_screen.current_fog_mode = fog_mode;
454
455         if (fog_mode == GR_FOGMODE_NONE) {
456                 return;
457         }
458
459         gr_screen.fog_near = fog_near;
460         gr_screen.fog_far = fog_far;
461
462         gr_init_color(&gr_screen.current_fog_color, r, g, b);
463 }
464
465 void gr_opengl2_zbuffer_clear(int mode)
466 {
467         if (mode) {
468                 Gr_zbuffering = 1;
469                 Gr_zbuffering_mode = GR_ZBUFF_FULL;
470                 Gr_global_zbuffering = 1;
471
472                 opengl2_set_state(TEXTURE_SOURCE_NONE, ALPHA_BLEND_NONE, ZBUFFER_TYPE_FULL);
473                 glClear(GL_DEPTH_BUFFER_BIT);
474         } else {
475                 Gr_zbuffering = 0;
476                 Gr_zbuffering_mode = GR_ZBUFF_NONE;
477                 Gr_global_zbuffering = 0;
478         }
479 }
480
481 void gr_opengl2_fade_in(int instantaneous)
482 {
483
484 }
485
486 void gr_opengl2_fade_out(int instantaneous)
487 {
488
489 }
490
491 void gr_opengl2_get_region(int front, int w, int h, ubyte *data)
492 {
493         opengl2_set_state(TEXTURE_SOURCE_NO_FILTERING, ALPHA_BLEND_NONE, ZBUFFER_TYPE_NONE);
494
495         GLenum pxtype = GL_UNSIGNED_SHORT_5_5_5_1;
496
497         if (gr_screen.bytes_per_pixel == 4) {
498                 pxtype = GL_UNSIGNED_BYTE;
499         }
500
501         glReadPixels(0, gr_screen.max_h-h-1, w, h, GL_RGBA, pxtype, data);
502 }
503
504 int gr_opengl2_save_screen()
505 {
506         gr_opengl_reset_clip();
507
508         if (GL_saved_screen_tex) {
509                 mprintf(( "Screen already saved!\n" ));
510                 return -1;
511         }
512
513         glGenTextures(1, &GL_saved_screen_tex);
514
515         if ( !GL_saved_screen_tex ) {
516                 mprintf(( "Couldn't create texture for saved screen!\n" ));
517                 return -1;
518         }
519
520         glBindTexture(GL_TEXTURE_2D, GL_saved_screen_tex);
521
522         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
523         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
524         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
525         glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
526
527         glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0,
528                         gr_screen.max_w, gr_screen.max_h, 0);
529
530         glBindTexture(GL_TEXTURE_2D, 0);
531
532         return 0;
533 }
534
535 void gr_opengl2_restore_screen(int)
536 {
537         gr_opengl_reset_clip();
538
539         if ( !GL_saved_screen_tex ) {
540                 gr_opengl_clear();
541                 return;
542         }
543
544         float x = 0.0f;
545         float y = 0.0f;
546         float w = i2fl(gr_screen.max_w);
547         float h = i2fl(gr_screen.max_h);
548
549         const float tex_coord[] = { 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f };   // y-flipped
550         const float ver_coord[] = { x, y, x, h, w, y, w, h };
551
552         opengl2_shader_use(PROG_TEX);
553
554         glVertexAttrib4f(SDRI_COLOR, 1.0f, 1.0f, 1.0f, 1.0f);
555
556         glEnableVertexAttribArray(SDRI_POSITION);
557         glVertexAttribPointer(SDRI_POSITION, 2, GL_FLOAT, GL_FALSE, 0, &ver_coord);
558
559         glEnableVertexAttribArray(SDRI_TEXCOORD);
560         glVertexAttribPointer(SDRI_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, &tex_coord);
561
562         glBindTexture(GL_TEXTURE_2D, GL_saved_screen_tex);
563
564         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
565
566         glBindTexture(GL_TEXTURE_2D, 0);
567
568         glDisableVertexAttribArray(SDRI_TEXCOORD);
569         glDisableVertexAttribArray(SDRI_POSITION);
570 }
571
572 void gr_opengl2_free_screen(int)
573 {
574         if (GL_saved_screen_tex) {
575                 glDeleteTextures(1, &GL_saved_screen_tex);
576                 GL_saved_screen_tex = 0;
577         }
578 }
579
580 void gr_opengl2_dump_frame_start(int first_frame, int frames_between_dumps)
581 {
582
583 }
584
585 void gr_opengl2_dump_frame_stop()
586 {
587
588 }
589
590 void gr_opengl2_dump_frame()
591 {
592
593 }
594
595 void gr_opengl2_set_viewport(int width, int height)
596 {
597         int w, h, x, y;
598
599         float ratio = gr_screen.max_w / i2fl(gr_screen.max_h);
600
601         w = width;
602         h = fl2i((width / ratio) + 0.5f);
603
604         if (h > height) {
605                 h = height;
606                 w = fl2i((height * ratio) + 0.5f);
607         }
608
609         x = (width - w) / 2;
610         y = (height - h) / 2;
611
612         GL_viewport_x = x;
613         GL_viewport_y = y;
614         GL_viewport_w = w;
615         GL_viewport_h = h;
616         GL_viewport_scale_w = w / i2fl(gr_screen.max_w);
617         GL_viewport_scale_h = h / i2fl(gr_screen.max_h);
618
619         opengl2_shader_update();
620 }