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