OpenGL: stipple support added to basic GLSL shader
[blender.git] / source / blender / windowmanager / intern / wm_stereo.c
1 /*
2  * ***** BEGIN GPL LICENSE BLOCK *****
3  *
4  * This program is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU General Public License
6  * as published by the Free Software Foundation; either version 2
7  * of the License, or (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17  *
18  * The Original Code is Copyright (C) 2015 by Blender Foundation
19  * All rights reserved.
20  *
21  * The Original Code is: all of this file.
22  *
23  * Contributor(s): Dalai Felinto
24  *
25  * ***** END GPL LICENSE BLOCK *****
26  */
27
28 /** \file blender/windowmanager/intern/wm_stereo.c
29  *  \ingroup wm
30  */
31
32
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include "DNA_listBase.h"
37
38 #include "RNA_access.h"
39
40 #include "MEM_guardedalloc.h"
41
42 #include "BLI_listbase.h"
43 #include "BLI_utildefines.h"
44
45 #include "BIF_gl.h"
46
47 #include "BKE_context.h"
48 #include "BKE_global.h"
49 #include "BKE_report.h"
50
51 #include "GHOST_C-api.h"
52
53 #include "ED_screen.h"
54
55 #include "GPU_glew.h"
56 #include "GPU_basic_shader.h"
57
58 #include "WM_api.h"
59 #include "WM_types.h"
60 #include "wm.h"
61 #include "wm_draw.h" /* wmDrawTriple */
62 #include "wm_window.h"
63
64 #include "UI_interface.h"
65 #include "UI_resources.h"
66
67 static void wm_method_draw_stereo3d_pageflip(wmWindow *win)
68 {
69         wmDrawData *drawdata;
70         int view;
71
72         for (view = 0; view < 2; view ++) {
73                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
74
75                 if (view == STEREO_LEFT_ID)
76                         glDrawBuffer(GL_BACK_LEFT);
77                 else //STEREO_RIGHT_ID
78                         glDrawBuffer(GL_BACK_RIGHT);
79
80                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
81         }
82
83         glDrawBuffer(GL_BACK);
84 }
85
86 static enum eStereo3dInterlaceType interlace_prev_type = -1;
87 static char interlace_prev_swap = -1;
88
89 static void wm_method_draw_stereo3d_interlace(wmWindow *win)
90 {
91         wmDrawData *drawdata;
92         int view;
93         bool flag;
94         bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0;
95         enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type;
96
97         for (view = 0; view < 2; view ++) {
98                 flag = swap ? !view : view;
99                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
100                 GPU_basic_shader_bind(GPU_SHADER_STIPPLE);
101                 switch (interlace_type) {
102                         case S3D_INTERLACE_ROW:
103                                 if (flag)
104                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW_SWAP);
105                                 else
106                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_ROW);
107                                 break;
108                         case S3D_INTERLACE_COLUMN:
109                                 if (flag)
110                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN_SWAP);
111                                 else
112                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_COLUMN);
113                                 break;
114                         case S3D_INTERLACE_CHECKERBOARD:
115                         default:
116                                 if (flag)
117                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER_SWAP);
118                                 else
119                                         GPU_basic_shader_stipple(GPU_SHADER_STIPPLE_S3D_INTERLACE_CHECKER);
120                                 break;
121                 }
122
123                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
124                 GPU_basic_shader_bind(GPU_SHADER_USE_COLOR);
125         }
126         interlace_prev_type = interlace_type;
127         interlace_prev_swap = swap;
128 }
129
130 static void wm_method_draw_stereo3d_anaglyph(wmWindow *win)
131 {
132         wmDrawData *drawdata;
133         int view, bit;
134
135         for (view = 0; view < 2; view ++) {
136                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
137
138                 bit = view + 1;
139                 switch (win->stereo3d_format->anaglyph_type) {
140                         case S3D_ANAGLYPH_REDCYAN:
141                                 glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
142                                             (2&bit) ? GL_TRUE : GL_FALSE,
143                                             (2&bit) ? GL_TRUE : GL_FALSE,
144                                             GL_FALSE);
145                                 break;
146                         case S3D_ANAGLYPH_GREENMAGENTA:
147                                 glColorMask((2&bit) ? GL_TRUE : GL_FALSE,
148                                             (1&bit) ? GL_TRUE : GL_FALSE,
149                                             (2&bit) ? GL_TRUE : GL_FALSE,
150                                             GL_FALSE);
151                                 break;
152                         case S3D_ANAGLYPH_YELLOWBLUE:
153                                 glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
154                                             (1&bit) ? GL_TRUE : GL_FALSE,
155                                             (2&bit) ? GL_TRUE : GL_FALSE,
156                                             GL_FALSE);
157                                 break;
158                 }
159
160                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
161
162                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
163         }
164 }
165
166 static void wm_method_draw_stereo3d_sidebyside(wmWindow *win)
167 {
168         wmDrawData *drawdata;
169         wmDrawTriple *triple;
170         float halfx, halfy, ratiox, ratioy;
171         float alpha = 1.0f;
172         int view;
173         int soffx;
174         bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
175
176         for (view = 0; view < 2; view ++) {
177                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
178                 triple = drawdata->triple;
179
180                 soffx = WM_window_pixels_x(win) * 0.5f;
181                 if (view == STEREO_LEFT_ID) {
182                         if (!cross_eyed)
183                                 soffx = 0;
184                 }
185                 else { //RIGHT_LEFT_ID
186                         if (cross_eyed)
187                                 soffx = 0;
188                 }
189
190                 glEnable(triple->target);
191
192                 const int sizex = triple->x;
193                 const int sizey = triple->y;
194
195                 /* wmOrtho for the screen has this same offset */
196                 ratiox = sizex;
197                 ratioy = sizey;
198                 halfx = GLA_PIXEL_OFS;
199                 halfy = GLA_PIXEL_OFS;
200
201                 /* texture rectangle has unnormalized coordinates */
202                 if (triple->target == GL_TEXTURE_2D) {
203                         ratiox /= triple->x;
204                         ratioy /= triple->y;
205                         halfx /= triple->x;
206                         halfy /= triple->y;
207                 }
208
209                 glBindTexture(triple->target, triple->bind);
210
211                 glColor4f(1.0f, 1.0f, 1.0f, alpha);
212                 glBegin(GL_QUADS);
213                 glTexCoord2f(halfx, halfy);
214                 glVertex2f(soffx, 0);
215
216                 glTexCoord2f(ratiox + halfx, halfy);
217                 glVertex2f(soffx + (sizex * 0.5f), 0);
218
219                 glTexCoord2f(ratiox + halfx, ratioy + halfy);
220                 glVertex2f(soffx + (sizex * 0.5f), sizey);
221
222                 glTexCoord2f(halfx, ratioy + halfy);
223                 glVertex2f(soffx, sizey);
224                 glEnd();
225
226                 glBindTexture(triple->target, 0);
227                 glDisable(triple->target);
228                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
229         }
230 }
231
232 static void wm_method_draw_stereo3d_topbottom(wmWindow *win)
233 {
234         wmDrawData *drawdata;
235         wmDrawTriple *triple;
236         float halfx, halfy, ratiox, ratioy;
237         float alpha = 1.0f;
238         int view;
239         int soffy;
240
241         for (view = 0; view < 2; view ++) {
242                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
243                 triple = drawdata->triple;
244
245                 if (view == STEREO_LEFT_ID) {
246                         soffy = WM_window_pixels_y(win) * 0.5f;
247                 }
248                 else { /* STEREO_RIGHT_ID */
249                         soffy = 0;
250                 }
251
252                 glEnable(triple->target);
253
254                 const int sizex = triple->x;
255                 const int sizey = triple->y;
256
257                 /* wmOrtho for the screen has this same offset */
258                 ratiox = sizex;
259                 ratioy = sizey;
260                 halfx = GLA_PIXEL_OFS;
261                 halfy = GLA_PIXEL_OFS;
262
263                 /* texture rectangle has unnormalized coordinates */
264                 if (triple->target == GL_TEXTURE_2D) {
265                         ratiox /= triple->x;
266                         ratioy /= triple->y;
267                         halfx /= triple->x;
268                         halfy /= triple->y;
269                 }
270
271                 glBindTexture(triple->target, triple->bind);
272
273                 glColor4f(1.0f, 1.0f, 1.0f, alpha);
274                 glBegin(GL_QUADS);
275                 glTexCoord2f(halfx, halfy);
276                 glVertex2f(0, soffy);
277
278                 glTexCoord2f(ratiox + halfx, halfy);
279                 glVertex2f(sizex, soffy);
280
281                 glTexCoord2f(ratiox + halfx, ratioy + halfy);
282                 glVertex2f(sizex, soffy + (sizey * 0.5f));
283
284                 glTexCoord2f(halfx, ratioy + halfy);
285                 glVertex2f(0, soffy + (sizey * 0.5f));
286                 glEnd();
287
288                 glBindTexture(triple->target, 0);
289                 glDisable(triple->target);
290                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
291         }
292 }
293
294 void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win)
295 {
296         switch (win->stereo3d_format->display_mode) {
297                 case S3D_DISPLAY_ANAGLYPH:
298                         wm_method_draw_stereo3d_anaglyph(win);
299                         break;
300                 case S3D_DISPLAY_INTERLACE:
301                         wm_method_draw_stereo3d_interlace(win);
302                         break;
303                 case S3D_DISPLAY_PAGEFLIP:
304                         wm_method_draw_stereo3d_pageflip(win);
305                         break;
306                 case S3D_DISPLAY_SIDEBYSIDE:
307                         wm_method_draw_stereo3d_sidebyside(win);
308                         break;
309                 case S3D_DISPLAY_TOPBOTTOM:
310                         wm_method_draw_stereo3d_topbottom(win);
311                         break;
312                 default:
313                         break;
314         }
315 }
316
317 static bool wm_stereo3d_quadbuffer_supported(void)
318 {
319         int gl_stereo = 0;
320         glGetBooleanv(GL_STEREO, (GLboolean *)&gl_stereo);
321         return gl_stereo != 0;
322 }
323
324 static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
325 {
326         return ELEM(stereo_display,
327                     S3D_DISPLAY_SIDEBYSIDE,
328                     S3D_DISPLAY_TOPBOTTOM);
329 }
330
331 bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
332 {
333         bScreen *screen = win->screen;
334
335         /* some 3d methods change the window arrangement, thus they shouldn't
336          * toggle on/off just because there is no 3d elements being drawn */
337         if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
338                 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
339         }
340
341         if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) {
342                 return false;
343         }
344
345         /* some 3d methods change the window arrangement, thus they shouldn't
346          * toggle on/off just because there is no 3d elements being drawn */
347         if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
348                 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
349         }
350
351         return true;
352 }
353
354 /************************** Stereo 3D operator **********************************/
355 typedef struct Stereo3dData {
356         Stereo3dFormat stereo3d_format;
357 } Stereo3dData;
358
359 static bool wm_stereo3d_set_properties(bContext *UNUSED(C), wmOperator *op)
360 {
361         Stereo3dData *s3dd = op->customdata;
362         Stereo3dFormat *s3d = &s3dd->stereo3d_format;
363         PropertyRNA *prop;
364         bool is_set = false;
365
366         prop = RNA_struct_find_property(op->ptr, "display_mode");
367         if (RNA_property_is_set(op->ptr, prop)) {
368                 s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
369                 is_set = true;
370         }
371
372         prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
373         if (RNA_property_is_set(op->ptr, prop)) {
374                 s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
375                 is_set = true;
376         }
377
378         prop = RNA_struct_find_property(op->ptr, "interlace_type");
379         if (RNA_property_is_set(op->ptr, prop)) {
380                 s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
381                 is_set = true;
382         }
383
384         prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
385         if (RNA_property_is_set(op->ptr, prop)) {
386                 if (RNA_property_boolean_get(op->ptr, prop))
387                         s3d->flag |= S3D_INTERLACE_SWAP;
388                 else
389                         s3d->flag &= ~S3D_INTERLACE_SWAP;
390                 is_set = true;
391         }
392
393         prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
394         if (RNA_property_is_set(op->ptr, prop)) {
395                 if (RNA_property_boolean_get(op->ptr, prop))
396                         s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
397                 else
398                         s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
399                 is_set = true;
400         }
401
402         return is_set;
403 }
404
405 static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
406 {
407         Stereo3dData *s3dd;
408         wmWindow *win = CTX_wm_window(C);
409
410         op->customdata = s3dd = MEM_callocN(sizeof(Stereo3dData), __func__);
411
412         /* store the original win stereo 3d settings in case of cancel */
413         s3dd->stereo3d_format = *win->stereo3d_format;
414 }
415
416 int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
417 {
418         wmWindowManager *wm = CTX_wm_manager(C);
419         wmWindow *win_src = CTX_wm_window(C);
420         wmWindow *win_dst = NULL;
421         const bool is_fullscreen = WM_window_is_fullscreen(win_src);
422         char prev_display_mode = win_src->stereo3d_format->display_mode;
423         Stereo3dData *s3dd;
424         bool ok = true;
425
426         if (G.background)
427                 return OPERATOR_CANCELLED;
428
429         if (op->customdata == NULL) {
430                 /* no invoke means we need to set the operator properties here */
431                 wm_stereo3d_set_init(C, op);
432                 wm_stereo3d_set_properties(C, op);
433         }
434
435         s3dd = op->customdata;
436         *win_src->stereo3d_format = s3dd->stereo3d_format;
437
438         if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
439             prev_display_mode != win_src->stereo3d_format->display_mode)
440         {
441                 /* in case the hardward supports pageflip but not the display */
442                 if ((win_dst = wm_window_copy_test(C, win_src))) {
443                         /* pass */
444                 }
445                 else {
446                         BKE_report(op->reports, RPT_ERROR,
447                                    "Failed to create a window without quad-buffer support, you may experience flickering");
448                         ok = false;
449                 }
450         }
451         else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
452                 /* ED_screen_duplicate() can't handle other cases yet T44688 */
453                 if (win_src->screen->state != SCREENNORMAL) {
454                         BKE_report(op->reports, RPT_ERROR,
455                                    "Failed to switch to Time Sequential mode when in fullscreen");
456                         ok = false;
457                 }
458                 /* pageflip requires a new window to be created with the proper OS flags */
459                 else if ((win_dst = wm_window_copy_test(C, win_src))) {
460                         if (wm_stereo3d_quadbuffer_supported()) {
461                                 BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
462                         }
463                         else {
464                                 wm_window_close(C, wm, win_dst);
465                                 win_dst = NULL;
466                                 BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
467                                 ok = false;
468                         }
469                 }
470                 else {
471                         BKE_report(op->reports, RPT_ERROR,
472                                    "Failed to create a window compatible with the time sequential display method");
473                         ok = false;
474                 }
475         }
476
477         if (wm_stereo3d_is_fullscreen_required(s3dd->stereo3d_format.display_mode)) {
478                 if (!is_fullscreen) {
479                         BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
480                 }
481         }
482
483         MEM_freeN(op->customdata);
484
485         if (ok) {
486                 if (win_dst) {
487                         wm_window_close(C, wm, win_src);
488                 }
489
490                 WM_event_add_notifier(C, NC_WINDOW, NULL);
491                 return OPERATOR_FINISHED;
492         }
493         else {
494                 /* without this, the popup won't be freed freed properly T44688 */
495                 CTX_wm_window_set(C, win_src);
496                 win_src->stereo3d_format->display_mode = prev_display_mode;
497                 return OPERATOR_CANCELLED;
498         }
499 }
500
501 int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
502 {
503         wm_stereo3d_set_init(C, op);
504
505         if (wm_stereo3d_set_properties(C, op))
506                 return wm_stereo3d_set_exec(C, op);
507         else
508                 return WM_operator_props_dialog_popup(C, op, 250, 100);
509 }
510
511 void wm_stereo3d_set_draw(bContext *UNUSED(C), wmOperator *op)
512 {
513         Stereo3dData *s3dd = op->customdata;
514         PointerRNA stereo3d_format_ptr;
515         uiLayout *layout = op->layout;
516         uiLayout *col;
517
518         RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format, &stereo3d_format_ptr);
519
520         col = uiLayoutColumn(layout, false);
521         uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
522
523         switch (s3dd->stereo3d_format.display_mode) {
524                 case S3D_DISPLAY_ANAGLYPH:
525                 {
526                         uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
527                         break;
528                 }
529                 case S3D_DISPLAY_INTERLACE:
530                 {
531                         uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
532                         uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
533                         break;
534                 }
535                 case S3D_DISPLAY_SIDEBYSIDE:
536                 {
537                         uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
538                         /* fall-through */
539                 }
540                 case S3D_DISPLAY_PAGEFLIP:
541                 case S3D_DISPLAY_TOPBOTTOM:
542                 default:
543                 {
544                         break;
545                 }
546         }
547 }
548
549 bool wm_stereo3d_set_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
550 {
551         /* the check function guarantees that the menu is updated to show the
552          * sub-options when an enum change (e.g., it shows the anaglyph options
553          * when anaglyph is on, and the interlace options when this is on */
554         return true;
555 }
556
557 void wm_stereo3d_set_cancel(bContext *UNUSED(C), wmOperator *op)
558 {
559         MEM_freeN(op->customdata);
560         op->customdata = NULL;
561 }