CMake: Disable some features when using MinGW and full cmake config
[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
57 #include "WM_api.h"
58 #include "WM_types.h"
59 #include "wm.h"
60 #include "wm_draw.h" /* wmDrawTriple */
61 #include "wm_window.h"
62
63 #include "UI_interface.h"
64 #include "UI_resources.h"
65
66 static void wm_method_draw_stereo3d_pageflip(wmWindow *win)
67 {
68         wmDrawData *drawdata;
69         int view;
70
71         for (view = 0; view < 2; view ++) {
72                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
73
74                 if (view == STEREO_LEFT_ID)
75                         glDrawBuffer(GL_BACK_LEFT);
76                 else //STEREO_RIGHT_ID
77                         glDrawBuffer(GL_BACK_RIGHT);
78
79                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
80         }
81
82         glDrawBuffer(GL_BACK);
83 }
84
85 static GLuint left_interlace_mask[32];
86 static GLuint right_interlace_mask[32];
87 static enum eStereo3dInterlaceType interlace_prev_type = -1;
88 static char interlace_prev_swap = -1;
89
90 static void wm_interlace_masks_create(wmWindow *win)
91 {
92         GLuint pattern;
93         char i;
94         bool swap = (win->stereo3d_format->flag & S3D_INTERLACE_SWAP) != 0;
95         enum eStereo3dInterlaceType interlace_type = win->stereo3d_format->interlace_type;
96
97         if (interlace_prev_type == interlace_type && interlace_prev_swap == swap)
98                 return;
99
100         switch (interlace_type) {
101                 case S3D_INTERLACE_ROW:
102                         pattern = 0x00000000;
103                         pattern = swap ? ~pattern : pattern;
104                         for (i = 0; i < 32; i += 2) {
105                                 left_interlace_mask[i] = pattern;
106                                 right_interlace_mask[i] = ~pattern;
107                         }
108                         for (i = 1; i < 32; i += 2) {
109                                 left_interlace_mask[i] = ~pattern;
110                                 right_interlace_mask[i] = pattern;
111                         }
112                         break;
113                 case S3D_INTERLACE_COLUMN:
114                         pattern = 0x55555555;
115                         pattern = swap ? ~pattern : pattern;
116                         for (i = 0; i < 32; i++) {
117                                 left_interlace_mask[i] = pattern;
118                                 right_interlace_mask[i] = ~pattern;
119                         }
120                         break;
121                 case S3D_INTERLACE_CHECKERBOARD:
122                 default:
123                         pattern = 0x55555555;
124                         pattern = swap ? ~pattern : pattern;
125                         for (i = 0; i < 32; i += 2) {
126                                 left_interlace_mask[i] = pattern;
127                                 right_interlace_mask[i] = ~pattern;
128                         }
129                         for (i = 1; i < 32; i += 2) {
130                                 left_interlace_mask[i] = ~pattern;
131                                 right_interlace_mask[i] = pattern;
132                         }
133                         break;
134         }
135         interlace_prev_type = interlace_type;
136         interlace_prev_swap = swap;
137 }
138
139 static void wm_method_draw_stereo3d_interlace(wmWindow *win)
140 {
141         wmDrawData *drawdata;
142         int view;
143
144         wm_interlace_masks_create(win);
145
146         for (view = 0; view < 2; view ++) {
147                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
148
149                 glEnable(GL_POLYGON_STIPPLE);
150                 glPolygonStipple(view ? (GLubyte *) right_interlace_mask : (GLubyte *) left_interlace_mask);
151
152                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
153                 glDisable(GL_POLYGON_STIPPLE);
154         }
155 }
156
157 static void wm_method_draw_stereo3d_anaglyph(wmWindow *win)
158 {
159         wmDrawData *drawdata;
160         int view, bit;
161
162         for (view = 0; view < 2; view ++) {
163                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
164
165                 bit = view + 1;
166                 switch (win->stereo3d_format->anaglyph_type) {
167                         case S3D_ANAGLYPH_REDCYAN:
168                                 glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
169                                             (2&bit) ? GL_TRUE : GL_FALSE,
170                                             (2&bit) ? GL_TRUE : GL_FALSE,
171                                             GL_FALSE);
172                                 break;
173                         case S3D_ANAGLYPH_GREENMAGENTA:
174                                 glColorMask((2&bit) ? GL_TRUE : GL_FALSE,
175                                             (1&bit) ? GL_TRUE : GL_FALSE,
176                                             (2&bit) ? GL_TRUE : GL_FALSE,
177                                             GL_FALSE);
178                                 break;
179                         case S3D_ANAGLYPH_YELLOWBLUE:
180                                 glColorMask((1&bit) ? GL_TRUE : GL_FALSE,
181                                             (1&bit) ? GL_TRUE : GL_FALSE,
182                                             (2&bit) ? GL_TRUE : GL_FALSE,
183                                             GL_FALSE);
184                                 break;
185                 }
186
187                 wm_triple_draw_textures(win, drawdata->triple, 1.0f);
188
189                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
190         }
191 }
192
193 static void wm_method_draw_stereo3d_sidebyside(wmWindow *win)
194 {
195         wmDrawData *drawdata;
196         wmDrawTriple *triple;
197         float halfx, halfy, ratiox, ratioy;
198         float alpha = 1.0f;
199         int view;
200         int soffx;
201         bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
202
203         for (view = 0; view < 2; view ++) {
204                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
205                 triple = drawdata->triple;
206
207                 soffx = WM_window_pixels_x(win) * 0.5f;
208                 if (view == STEREO_LEFT_ID) {
209                         if (!cross_eyed)
210                                 soffx = 0;
211                 }
212                 else { //RIGHT_LEFT_ID
213                         if (cross_eyed)
214                                 soffx = 0;
215                 }
216
217                 glEnable(triple->target);
218
219                 const int sizex = triple->x;
220                 const int sizey = triple->y;
221
222                 /* wmOrtho for the screen has this same offset */
223                 ratiox = sizex;
224                 ratioy = sizey;
225                 halfx = GLA_PIXEL_OFS;
226                 halfy = GLA_PIXEL_OFS;
227
228                 /* texture rectangle has unnormalized coordinates */
229                 if (triple->target == GL_TEXTURE_2D) {
230                         ratiox /= triple->x;
231                         ratioy /= triple->y;
232                         halfx /= triple->x;
233                         halfy /= triple->y;
234                 }
235
236                 glBindTexture(triple->target, triple->bind);
237
238                 glColor4f(1.0f, 1.0f, 1.0f, alpha);
239                 glBegin(GL_QUADS);
240                 glTexCoord2f(halfx, halfy);
241                 glVertex2f(soffx, 0);
242
243                 glTexCoord2f(ratiox + halfx, halfy);
244                 glVertex2f(soffx + (sizex * 0.5f), 0);
245
246                 glTexCoord2f(ratiox + halfx, ratioy + halfy);
247                 glVertex2f(soffx + (sizex * 0.5f), sizey);
248
249                 glTexCoord2f(halfx, ratioy + halfy);
250                 glVertex2f(soffx, sizey);
251                 glEnd();
252
253                 glBindTexture(triple->target, 0);
254                 glDisable(triple->target);
255                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
256         }
257 }
258
259 static void wm_method_draw_stereo3d_topbottom(wmWindow *win)
260 {
261         wmDrawData *drawdata;
262         wmDrawTriple *triple;
263         float halfx, halfy, ratiox, ratioy;
264         float alpha = 1.0f;
265         int view;
266         int soffy;
267
268         for (view = 0; view < 2; view ++) {
269                 drawdata = BLI_findlink(&win->drawdata, (view * 2) + 1);
270                 triple = drawdata->triple;
271
272                 if (view == STEREO_LEFT_ID) {
273                         soffy = WM_window_pixels_y(win) * 0.5f;
274                 }
275                 else { /* STEREO_RIGHT_ID */
276                         soffy = 0;
277                 }
278
279                 glEnable(triple->target);
280
281                 const int sizex = triple->x;
282                 const int sizey = triple->y;
283
284                 /* wmOrtho for the screen has this same offset */
285                 ratiox = sizex;
286                 ratioy = sizey;
287                 halfx = GLA_PIXEL_OFS;
288                 halfy = GLA_PIXEL_OFS;
289
290                 /* texture rectangle has unnormalized coordinates */
291                 if (triple->target == GL_TEXTURE_2D) {
292                         ratiox /= triple->x;
293                         ratioy /= triple->y;
294                         halfx /= triple->x;
295                         halfy /= triple->y;
296                 }
297
298                 glBindTexture(triple->target, triple->bind);
299
300                 glColor4f(1.0f, 1.0f, 1.0f, alpha);
301                 glBegin(GL_QUADS);
302                 glTexCoord2f(halfx, halfy);
303                 glVertex2f(0, soffy);
304
305                 glTexCoord2f(ratiox + halfx, halfy);
306                 glVertex2f(sizex, soffy);
307
308                 glTexCoord2f(ratiox + halfx, ratioy + halfy);
309                 glVertex2f(sizex, soffy + (sizey * 0.5f));
310
311                 glTexCoord2f(halfx, ratioy + halfy);
312                 glVertex2f(0, soffy + (sizey * 0.5f));
313                 glEnd();
314
315                 glBindTexture(triple->target, 0);
316                 glDisable(triple->target);
317                 glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
318         }
319 }
320
321 void wm_method_draw_stereo3d(const bContext *UNUSED(C), wmWindow *win)
322 {
323         switch (win->stereo3d_format->display_mode) {
324                 case S3D_DISPLAY_ANAGLYPH:
325                         wm_method_draw_stereo3d_anaglyph(win);
326                         break;
327                 case S3D_DISPLAY_INTERLACE:
328                         wm_method_draw_stereo3d_interlace(win);
329                         break;
330                 case S3D_DISPLAY_PAGEFLIP:
331                         wm_method_draw_stereo3d_pageflip(win);
332                         break;
333                 case S3D_DISPLAY_SIDEBYSIDE:
334                         wm_method_draw_stereo3d_sidebyside(win);
335                         break;
336                 case S3D_DISPLAY_TOPBOTTOM:
337                         wm_method_draw_stereo3d_topbottom(win);
338                         break;
339                 default:
340                         break;
341         }
342 }
343
344 static bool wm_stereo3d_quadbuffer_supported(void)
345 {
346         int gl_stereo = 0;
347         glGetBooleanv(GL_STEREO, (GLboolean *)&gl_stereo);
348         return gl_stereo != 0;
349 }
350
351 static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
352 {
353         return ELEM(stereo_display,
354                     S3D_DISPLAY_SIDEBYSIDE,
355                     S3D_DISPLAY_TOPBOTTOM);
356 }
357
358 bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
359 {
360         bScreen *screen = win->screen;
361
362         /* some 3d methods change the window arrangement, thus they shouldn't
363          * toggle on/off just because there is no 3d elements being drawn */
364         if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
365                 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
366         }
367
368         if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen) == false)) {
369                 return false;
370         }
371
372         /* some 3d methods change the window arrangement, thus they shouldn't
373          * toggle on/off just because there is no 3d elements being drawn */
374         if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
375                 return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
376         }
377
378         return true;
379 }
380
381 /************************** Stereo 3D operator **********************************/
382 typedef struct Stereo3dData {
383         Stereo3dFormat stereo3d_format;
384 } Stereo3dData;
385
386 static bool wm_stereo3d_set_properties(bContext *UNUSED(C), wmOperator *op)
387 {
388         Stereo3dData *s3dd = op->customdata;
389         Stereo3dFormat *s3d = &s3dd->stereo3d_format;
390         PropertyRNA *prop;
391         bool is_set = false;
392
393         prop = RNA_struct_find_property(op->ptr, "display_mode");
394         if (RNA_property_is_set(op->ptr, prop)) {
395                 s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
396                 is_set = true;
397         }
398
399         prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
400         if (RNA_property_is_set(op->ptr, prop)) {
401                 s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
402                 is_set = true;
403         }
404
405         prop = RNA_struct_find_property(op->ptr, "interlace_type");
406         if (RNA_property_is_set(op->ptr, prop)) {
407                 s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
408                 is_set = true;
409         }
410
411         prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
412         if (RNA_property_is_set(op->ptr, prop)) {
413                 if (RNA_property_boolean_get(op->ptr, prop))
414                         s3d->flag |= S3D_INTERLACE_SWAP;
415                 else
416                         s3d->flag &= ~S3D_INTERLACE_SWAP;
417                 is_set = true;
418         }
419
420         prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
421         if (RNA_property_is_set(op->ptr, prop)) {
422                 if (RNA_property_boolean_get(op->ptr, prop))
423                         s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
424                 else
425                         s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
426                 is_set = true;
427         }
428
429         return is_set;
430 }
431
432 static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
433 {
434         Stereo3dData *s3dd;
435         wmWindow *win = CTX_wm_window(C);
436
437         op->customdata = s3dd = MEM_callocN(sizeof(Stereo3dData), __func__);
438
439         /* store the original win stereo 3d settings in case of cancel */
440         s3dd->stereo3d_format = *win->stereo3d_format;
441 }
442
443 int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
444 {
445         wmWindowManager *wm = CTX_wm_manager(C);
446         wmWindow *win_src = CTX_wm_window(C);
447         wmWindow *win_dst = NULL;
448         const bool is_fullscreen = WM_window_is_fullscreen(win_src);
449         char prev_display_mode = win_src->stereo3d_format->display_mode;
450         Stereo3dData *s3dd;
451         bool ok = true;
452
453         if (G.background)
454                 return OPERATOR_CANCELLED;
455
456         if (op->customdata == NULL) {
457                 /* no invoke means we need to set the operator properties here */
458                 wm_stereo3d_set_init(C, op);
459                 wm_stereo3d_set_properties(C, op);
460         }
461
462         s3dd = op->customdata;
463         *win_src->stereo3d_format = s3dd->stereo3d_format;
464
465         if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
466             prev_display_mode != win_src->stereo3d_format->display_mode)
467         {
468                 /* in case the hardward supports pageflip but not the display */
469                 if ((win_dst = wm_window_copy_test(C, win_src))) {
470                         /* pass */
471                 }
472                 else {
473                         BKE_report(op->reports, RPT_ERROR,
474                                    "Failed to create a window without quad-buffer support, you may experience flickering");
475                         ok = false;
476                 }
477         }
478         else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
479                 /* ED_screen_duplicate() can't handle other cases yet T44688 */
480                 if (win_src->screen->state != SCREENNORMAL) {
481                         BKE_report(op->reports, RPT_ERROR,
482                                    "Failed to switch to Time Sequential mode when in fullscreen");
483                         ok = false;
484                 }
485                 /* pageflip requires a new window to be created with the proper OS flags */
486                 else if ((win_dst = wm_window_copy_test(C, win_src))) {
487                         if (wm_stereo3d_quadbuffer_supported()) {
488                                 BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
489                         }
490                         else {
491                                 wm_window_close(C, wm, win_dst);
492                                 win_dst = NULL;
493                                 BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
494                                 ok = false;
495                         }
496                 }
497                 else {
498                         BKE_report(op->reports, RPT_ERROR,
499                                    "Failed to create a window compatible with the time sequential display method");
500                         ok = false;
501                 }
502         }
503
504         if (wm_stereo3d_is_fullscreen_required(s3dd->stereo3d_format.display_mode)) {
505                 if (!is_fullscreen) {
506                         BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
507                 }
508         }
509
510         MEM_freeN(op->customdata);
511
512         if (ok) {
513                 if (win_dst) {
514                         wm_window_close(C, wm, win_src);
515                 }
516
517                 WM_event_add_notifier(C, NC_WINDOW, NULL);
518                 return OPERATOR_FINISHED;
519         }
520         else {
521                 /* without this, the popup won't be freed freed properly T44688 */
522                 CTX_wm_window_set(C, win_src);
523                 win_src->stereo3d_format->display_mode = prev_display_mode;
524                 return OPERATOR_CANCELLED;
525         }
526 }
527
528 int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
529 {
530         wm_stereo3d_set_init(C, op);
531
532         if (wm_stereo3d_set_properties(C, op))
533                 return wm_stereo3d_set_exec(C, op);
534         else
535                 return WM_operator_props_dialog_popup(C, op, 250, 100);
536 }
537
538 void wm_stereo3d_set_draw(bContext *UNUSED(C), wmOperator *op)
539 {
540         Stereo3dData *s3dd = op->customdata;
541         PointerRNA stereo3d_format_ptr;
542         uiLayout *layout = op->layout;
543         uiLayout *col;
544
545         RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format, &stereo3d_format_ptr);
546
547         col = uiLayoutColumn(layout, false);
548         uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
549
550         switch (s3dd->stereo3d_format.display_mode) {
551                 case S3D_DISPLAY_ANAGLYPH:
552                 {
553                         uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
554                         break;
555                 }
556                 case S3D_DISPLAY_INTERLACE:
557                 {
558                         uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
559                         uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
560                         break;
561                 }
562                 case S3D_DISPLAY_SIDEBYSIDE:
563                 {
564                         uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
565                         /* fall-through */
566                 }
567                 case S3D_DISPLAY_PAGEFLIP:
568                 case S3D_DISPLAY_TOPBOTTOM:
569                 default:
570                 {
571                         break;
572                 }
573         }
574 }
575
576 bool wm_stereo3d_set_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
577 {
578         /* the check function guarantees that the menu is updated to show the
579          * sub-options when an enum change (e.g., it shows the anaglyph options
580          * when anaglyph is on, and the interlace options when this is on */
581         return true;
582 }
583
584 void wm_stereo3d_set_cancel(bContext *UNUSED(C), wmOperator *op)
585 {
586         MEM_freeN(op->customdata);
587         op->customdata = NULL;
588 }