1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2015 by Blender Foundation
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup wm
22  */
23 
24 #include <stdlib.h>
25 #include <string.h>
26 
27 #include "DNA_listBase.h"
28 
29 #include "RNA_access.h"
30 
31 #include "MEM_guardedalloc.h"
32 
33 #include "BLI_utildefines.h"
34 
35 #include "BKE_context.h"
36 #include "BKE_global.h"
37 #include "BKE_report.h"
38 
39 #include "GHOST_C-api.h"
40 
41 #include "ED_screen.h"
42 
43 #include "GPU_capabilities.h"
44 #include "GPU_immediate.h"
45 #include "GPU_texture.h"
46 #include "GPU_viewport.h"
47 
48 #include "WM_api.h"
49 #include "WM_types.h"
50 #include "wm.h"
51 #include "wm_draw.h"
52 #include "wm_window.h"
53 
54 #include "UI_interface.h"
55 #include "UI_resources.h"
56 
wm_stereo3d_draw_sidebyside(wmWindow * win,int view)57 void wm_stereo3d_draw_sidebyside(wmWindow *win, int view)
58 {
59   bool cross_eyed = (win->stereo3d_format->flag & S3D_SIDEBYSIDE_CROSSEYED) != 0;
60 
61   GPUVertFormat *format = immVertexFormat();
62   uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
63   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
64 
65   immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
66 
67   int soffx = WM_window_pixels_x(win) * 0.5f;
68   if (view == STEREO_LEFT_ID) {
69     if (!cross_eyed) {
70       soffx = 0;
71     }
72   }
73   else { /* #RIGHT_LEFT_ID */
74     if (cross_eyed) {
75       soffx = 0;
76     }
77   }
78 
79   const int sizex = WM_window_pixels_x(win);
80   const int sizey = WM_window_pixels_y(win);
81 
82   /* wmOrtho for the screen has this same offset */
83   const float halfx = GLA_PIXEL_OFS / sizex;
84   const float halfy = GLA_PIXEL_OFS / sizex;
85 
86   immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
87 
88   immBegin(GPU_PRIM_TRI_FAN, 4);
89 
90   immAttr2f(texcoord, halfx, halfy);
91   immVertex2f(pos, soffx, 0.0f);
92 
93   immAttr2f(texcoord, 1.0f + halfx, halfy);
94   immVertex2f(pos, soffx + (sizex * 0.5f), 0.0f);
95 
96   immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
97   immVertex2f(pos, soffx + (sizex * 0.5f), sizey);
98 
99   immAttr2f(texcoord, halfx, 1.0f + halfy);
100   immVertex2f(pos, soffx, sizey);
101 
102   immEnd();
103 
104   immUnbindProgram();
105 }
106 
wm_stereo3d_draw_topbottom(wmWindow * win,int view)107 void wm_stereo3d_draw_topbottom(wmWindow *win, int view)
108 {
109   GPUVertFormat *format = immVertexFormat();
110   uint texcoord = GPU_vertformat_attr_add(format, "texCoord", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
111   uint pos = GPU_vertformat_attr_add(format, "pos", GPU_COMP_F32, 2, GPU_FETCH_FLOAT);
112 
113   immBindBuiltinProgram(GPU_SHADER_2D_IMAGE);
114 
115   int soffy;
116   if (view == STEREO_LEFT_ID) {
117     soffy = WM_window_pixels_y(win) * 0.5f;
118   }
119   else { /* STEREO_RIGHT_ID */
120     soffy = 0;
121   }
122 
123   const int sizex = WM_window_pixels_x(win);
124   const int sizey = WM_window_pixels_y(win);
125 
126   /* wmOrtho for the screen has this same offset */
127   const float halfx = GLA_PIXEL_OFS / sizex;
128   const float halfy = GLA_PIXEL_OFS / sizex;
129 
130   immUniform1i("image", 0); /* texture is already bound to GL_TEXTURE0 unit */
131 
132   immBegin(GPU_PRIM_TRI_FAN, 4);
133 
134   immAttr2f(texcoord, halfx, halfy);
135   immVertex2f(pos, 0.0f, soffy);
136 
137   immAttr2f(texcoord, 1.0f + halfx, halfy);
138   immVertex2f(pos, sizex, soffy);
139 
140   immAttr2f(texcoord, 1.0f + halfx, 1.0f + halfy);
141   immVertex2f(pos, sizex, soffy + (sizey * 0.5f));
142 
143   immAttr2f(texcoord, halfx, 1.0f + halfy);
144   immVertex2f(pos, 0.0f, soffy + (sizey * 0.5f));
145 
146   immEnd();
147 
148   immUnbindProgram();
149 }
150 
wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)151 static bool wm_stereo3d_is_fullscreen_required(eStereoDisplayMode stereo_display)
152 {
153   return ELEM(stereo_display, S3D_DISPLAY_SIDEBYSIDE, S3D_DISPLAY_TOPBOTTOM);
154 }
155 
WM_stereo3d_enabled(wmWindow * win,bool skip_stereo3d_check)156 bool WM_stereo3d_enabled(wmWindow *win, bool skip_stereo3d_check)
157 {
158   const bScreen *screen = WM_window_get_active_screen(win);
159   const Scene *scene = WM_window_get_active_scene(win);
160 
161   /* some 3d methods change the window arrangement, thus they shouldn't
162    * toggle on/off just because there is no 3d elements being drawn */
163   if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
164     return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
165   }
166 
167   if ((skip_stereo3d_check == false) && (ED_screen_stereo3d_required(screen, scene) == false)) {
168     return false;
169   }
170 
171   /* some 3d methods change the window arrangement, thus they shouldn't
172    * toggle on/off just because there is no 3d elements being drawn */
173   if (wm_stereo3d_is_fullscreen_required(win->stereo3d_format->display_mode)) {
174     return GHOST_GetWindowState(win->ghostwin) == GHOST_kWindowStateFullScreen;
175   }
176 
177   return true;
178 }
179 
180 /**
181  * If needed, adjust \a r_mouse_xy
182  * so that drawn cursor and handled mouse position are matching visually.
183  */
wm_stereo3d_mouse_offset_apply(wmWindow * win,int * r_mouse_xy)184 void wm_stereo3d_mouse_offset_apply(wmWindow *win, int *r_mouse_xy)
185 {
186   if (!WM_stereo3d_enabled(win, false)) {
187     return;
188   }
189 
190   if (win->stereo3d_format->display_mode == S3D_DISPLAY_SIDEBYSIDE) {
191     const int half_x = win->sizex / 2;
192     /* right half of the screen */
193     if (r_mouse_xy[0] > half_x) {
194       r_mouse_xy[0] -= half_x;
195     }
196     r_mouse_xy[0] *= 2;
197   }
198   else if (win->stereo3d_format->display_mode == S3D_DISPLAY_TOPBOTTOM) {
199     const int half_y = win->sizey / 2;
200     /* upper half of the screen */
201     if (r_mouse_xy[1] > half_y) {
202       r_mouse_xy[1] -= half_y;
203     }
204     r_mouse_xy[1] *= 2;
205   }
206 }
207 
208 /************************** Stereo 3D operator **********************************/
209 typedef struct Stereo3dData {
210   Stereo3dFormat stereo3d_format;
211 } Stereo3dData;
212 
wm_stereo3d_set_properties(bContext * UNUSED (C),wmOperator * op)213 static bool wm_stereo3d_set_properties(bContext *UNUSED(C), wmOperator *op)
214 {
215   Stereo3dData *s3dd = op->customdata;
216   Stereo3dFormat *s3d = &s3dd->stereo3d_format;
217   PropertyRNA *prop;
218   bool is_set = false;
219 
220   prop = RNA_struct_find_property(op->ptr, "display_mode");
221   if (RNA_property_is_set(op->ptr, prop)) {
222     s3d->display_mode = RNA_property_enum_get(op->ptr, prop);
223     is_set = true;
224   }
225 
226   prop = RNA_struct_find_property(op->ptr, "anaglyph_type");
227   if (RNA_property_is_set(op->ptr, prop)) {
228     s3d->anaglyph_type = RNA_property_enum_get(op->ptr, prop);
229     is_set = true;
230   }
231 
232   prop = RNA_struct_find_property(op->ptr, "interlace_type");
233   if (RNA_property_is_set(op->ptr, prop)) {
234     s3d->interlace_type = RNA_property_enum_get(op->ptr, prop);
235     is_set = true;
236   }
237 
238   prop = RNA_struct_find_property(op->ptr, "use_interlace_swap");
239   if (RNA_property_is_set(op->ptr, prop)) {
240     if (RNA_property_boolean_get(op->ptr, prop)) {
241       s3d->flag |= S3D_INTERLACE_SWAP;
242     }
243     else {
244       s3d->flag &= ~S3D_INTERLACE_SWAP;
245     }
246     is_set = true;
247   }
248 
249   prop = RNA_struct_find_property(op->ptr, "use_sidebyside_crosseyed");
250   if (RNA_property_is_set(op->ptr, prop)) {
251     if (RNA_property_boolean_get(op->ptr, prop)) {
252       s3d->flag |= S3D_SIDEBYSIDE_CROSSEYED;
253     }
254     else {
255       s3d->flag &= ~S3D_SIDEBYSIDE_CROSSEYED;
256     }
257     is_set = true;
258   }
259 
260   return is_set;
261 }
262 
wm_stereo3d_set_init(bContext * C,wmOperator * op)263 static void wm_stereo3d_set_init(bContext *C, wmOperator *op)
264 {
265   wmWindow *win = CTX_wm_window(C);
266 
267   Stereo3dData *s3dd = MEM_callocN(sizeof(Stereo3dData), __func__);
268   op->customdata = s3dd;
269 
270   /* store the original win stereo 3d settings in case of cancel */
271   s3dd->stereo3d_format = *win->stereo3d_format;
272 }
273 
wm_stereo3d_set_exec(bContext * C,wmOperator * op)274 int wm_stereo3d_set_exec(bContext *C, wmOperator *op)
275 {
276   wmWindowManager *wm = CTX_wm_manager(C);
277   wmWindow *win_src = CTX_wm_window(C);
278   wmWindow *win_dst = NULL;
279   const bool is_fullscreen = WM_window_is_fullscreen(win_src);
280   char prev_display_mode = win_src->stereo3d_format->display_mode;
281   bool ok = true;
282 
283   if (G.background) {
284     return OPERATOR_CANCELLED;
285   }
286 
287   if (op->customdata == NULL) {
288     /* no invoke means we need to set the operator properties here */
289     wm_stereo3d_set_init(C, op);
290     wm_stereo3d_set_properties(C, op);
291   }
292 
293   Stereo3dData *s3dd = op->customdata;
294   *win_src->stereo3d_format = s3dd->stereo3d_format;
295 
296   if (prev_display_mode == S3D_DISPLAY_PAGEFLIP &&
297       prev_display_mode != win_src->stereo3d_format->display_mode) {
298     /* in case the hardware supports pageflip but not the display */
299     if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
300       /* pass */
301     }
302     else {
303       BKE_report(
304           op->reports,
305           RPT_ERROR,
306           "Failed to create a window without quad-buffer support, you may experience flickering");
307       ok = false;
308     }
309   }
310   else if (win_src->stereo3d_format->display_mode == S3D_DISPLAY_PAGEFLIP) {
311     const bScreen *screen = WM_window_get_active_screen(win_src);
312 
313     /* ED_workspace_layout_duplicate() can't handle other cases yet T44688 */
314     if (screen->state != SCREENNORMAL) {
315       BKE_report(
316           op->reports, RPT_ERROR, "Failed to switch to Time Sequential mode when in fullscreen");
317       ok = false;
318     }
319     /* pageflip requires a new window to be created with the proper OS flags */
320     else if ((win_dst = wm_window_copy_test(C, win_src, false, false))) {
321       if (GPU_stereo_quadbuffer_support()) {
322         BKE_report(op->reports, RPT_INFO, "Quad-buffer window successfully created");
323       }
324       else {
325         wm_window_close(C, wm, win_dst);
326         win_dst = NULL;
327         BKE_report(op->reports, RPT_ERROR, "Quad-buffer not supported by the system");
328         ok = false;
329       }
330     }
331     else {
332       BKE_report(op->reports,
333                  RPT_ERROR,
334                  "Failed to create a window compatible with the time sequential display method");
335       ok = false;
336     }
337   }
338 
339   if (wm_stereo3d_is_fullscreen_required(s3dd->stereo3d_format.display_mode)) {
340     if (!is_fullscreen) {
341       BKE_report(op->reports, RPT_INFO, "Stereo 3D Mode requires the window to be fullscreen");
342     }
343   }
344 
345   MEM_freeN(op->customdata);
346 
347   if (ok) {
348     if (win_dst) {
349       wm_window_close(C, wm, win_src);
350     }
351 
352     WM_event_add_notifier(C, NC_WINDOW, NULL);
353     return OPERATOR_FINISHED;
354   }
355 
356   /* without this, the popup won't be freed freed properly T44688 */
357   CTX_wm_window_set(C, win_src);
358   win_src->stereo3d_format->display_mode = prev_display_mode;
359   return OPERATOR_CANCELLED;
360 }
361 
wm_stereo3d_set_invoke(bContext * C,wmOperator * op,const wmEvent * UNUSED (event))362 int wm_stereo3d_set_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
363 {
364   wm_stereo3d_set_init(C, op);
365 
366   if (wm_stereo3d_set_properties(C, op)) {
367     return wm_stereo3d_set_exec(C, op);
368   }
369   return WM_operator_props_dialog_popup(C, op, 300);
370 }
371 
wm_stereo3d_set_draw(bContext * UNUSED (C),wmOperator * op)372 void wm_stereo3d_set_draw(bContext *UNUSED(C), wmOperator *op)
373 {
374   Stereo3dData *s3dd = op->customdata;
375   PointerRNA stereo3d_format_ptr;
376   uiLayout *layout = op->layout;
377   uiLayout *col;
378 
379   RNA_pointer_create(NULL, &RNA_Stereo3dDisplay, &s3dd->stereo3d_format, &stereo3d_format_ptr);
380 
381   uiLayoutSetPropSep(layout, true);
382   uiLayoutSetPropDecorate(layout, false);
383 
384   col = uiLayoutColumn(layout, false);
385   uiItemR(col, &stereo3d_format_ptr, "display_mode", 0, NULL, ICON_NONE);
386 
387   switch (s3dd->stereo3d_format.display_mode) {
388     case S3D_DISPLAY_ANAGLYPH: {
389       uiItemR(col, &stereo3d_format_ptr, "anaglyph_type", 0, NULL, ICON_NONE);
390       break;
391     }
392     case S3D_DISPLAY_INTERLACE: {
393       uiItemR(col, &stereo3d_format_ptr, "interlace_type", 0, NULL, ICON_NONE);
394       uiItemR(col, &stereo3d_format_ptr, "use_interlace_swap", 0, NULL, ICON_NONE);
395       break;
396     }
397     case S3D_DISPLAY_SIDEBYSIDE: {
398       uiItemR(col, &stereo3d_format_ptr, "use_sidebyside_crosseyed", 0, NULL, ICON_NONE);
399       /* fall-through */
400     }
401     case S3D_DISPLAY_PAGEFLIP:
402     case S3D_DISPLAY_TOPBOTTOM:
403     default: {
404       break;
405     }
406   }
407 }
408 
wm_stereo3d_set_check(bContext * UNUSED (C),wmOperator * UNUSED (op))409 bool wm_stereo3d_set_check(bContext *UNUSED(C), wmOperator *UNUSED(op))
410 {
411   /* the check function guarantees that the menu is updated to show the
412    * sub-options when an enum change (e.g., it shows the anaglyph options
413    * when anaglyph is on, and the interlace options when this is on */
414   return true;
415 }
416 
wm_stereo3d_set_cancel(bContext * UNUSED (C),wmOperator * op)417 void wm_stereo3d_set_cancel(bContext *UNUSED(C), wmOperator *op)
418 {
419   MEM_freeN(op->customdata);
420   op->customdata = NULL;
421 }
422